From 308d99caa8b33a1b2eef17b5a598c5fe5fc63c84 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Thu, 15 Oct 2020 11:26:07 -0700 Subject: [PATCH 001/114] Merging Recent SQL Executor changes --- lux/action/correlation.py | 23 +-- lux/action/univariate.py | 3 - lux/core/frame.py | 180 ++++-------------- lux/executor/Executor.py | 16 +- lux/executor/SQLExecutor.py | 354 +++++++++++++++++++++++++----------- lux/processor/Validator.py | 3 - 6 files changed, 284 insertions(+), 295 deletions(-) diff --git a/lux/action/correlation.py b/lux/action/correlation.py index b5f23fee..f63a5ba2 100644 --- a/lux/action/correlation.py +++ b/lux/action/correlation.py @@ -1,22 +1,10 @@ -# 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. - import lux from lux.interestingness.interestingness import interestingness from lux.processor.Compiler import Compiler from lux.core.frame import LuxDataFrame from lux.vis.VisList import VisList +# for benchmarking +import time from lux.utils import utils @@ -24,15 +12,12 @@ def correlation(ldf: LuxDataFrame, ignore_transpose: bool = True): ''' Generates bivariate visualizations that represent all pairwise relationships in the data. - Parameters ---------- ldf : LuxDataFrame LuxDataFrame with underspecified intent. - ignore_transpose: bool Boolean flag to ignore pairs of attributes whose transpose are already computed (i.e., {X,Y} will be ignored if {Y,X} is already computed) - Returns ------- recommendations : Dict[str,obj] @@ -47,7 +32,7 @@ def correlation(ldf: LuxDataFrame, ignore_transpose: bool = True): recommendation = {"action": "Correlation", "description": "Show relationships between two

quantitative

attributes."} ignore_rec_flag = False - if (len(ldf)<5): # Doesn't make sense to compute correlation if less than 4 data values + if (len(ldf)<5 and ldf.executor_type == "Pandas"): # Doesn't make sense to compute correlation if less than 4 data values ignore_rec_flag = True # Then use the data populated in the vis list to compute score for vis in vlist: @@ -78,4 +63,4 @@ def check_transpose_not_computed(vlist: VisList, a: str, b: str): if (len(transpose_exist) > 0): return transpose_exist[0].score == -1 else: - return False + return False \ No newline at end of file diff --git a/lux/action/univariate.py b/lux/action/univariate.py index 2735348b..c4b289b6 100644 --- a/lux/action/univariate.py +++ b/lux/action/univariate.py @@ -19,15 +19,12 @@ def univariate(ldf, data_type_constraint="quantitative"): ''' Generates bar chart distributions of different attributes in the dataframe. - Parameters ---------- ldf : lux.core.frame LuxDataFrame with underspecified intent. - data_type_constraint: str Controls the type of distribution chart that will be rendered. - Returns ------- recommendations : Dict[str,obj] diff --git a/lux/core/frame.py b/lux/core/frame.py index 9da8a7c5..3866e824 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -1,26 +1,15 @@ -# 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. - import pandas as pd from lux.core.series import LuxSeries from lux.vis.Clause import Clause from lux.vis.Vis import Vis from lux.vis.VisList import VisList from lux.history.history import History +from lux.utils.date_utils import is_datetime_series from lux.utils.message import Message from lux.utils.utils import check_import_lux_widget -from typing import Dict, Union, List, Callable +#import for benchmarking +import time +from typing import Optional, Dict, Union, List, Callable import warnings class LuxDataFrame(pd.DataFrame): ''' @@ -29,14 +18,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) @@ -46,7 +34,6 @@ def __init__(self,*args, **kw): self.SQLconnection = "" self.table_name = "" - self._sampled = None self._default_pandas_display = True self._toggle_pandas_display = True self._plot_config = None @@ -76,7 +63,7 @@ def history(self): return self._history def maintain_metadata(self): if (not hasattr(self,"_metadata_fresh") or not self._metadata_fresh ): # Check that metadata has not yet been computed - if (len(self)>0): #only compute metadata information if the dataframe is non-empty + if (len(self)>0 or self.executor_type == "SQL"): #only compute metadata information if the dataframe is non-empty self.executor.compute_stats(self) self.executor.compute_dataset_metadata(self) self._infer_structure() @@ -86,8 +73,7 @@ def expire_recs(self): self.recommendation = None self.current_vis = None self._widget = None - self._rec_info = None - self._sampled = None + self._rec_info= None def expire_metadata(self): # Set metadata as null self._metadata_fresh = False @@ -103,11 +89,14 @@ def expire_metadata(self): ##################### ## Override Pandas ## ##################### + # def __finalize__(self,other, method: Optional[str] = None, **kwargs): + # print ("lux finalize") + # super(LuxDataFrame, self).__finalize__(other,method,**kwargs) + # self.expire_metadata() def __getattr__(self, name): - ret_value = super(LuxDataFrame, self).__getattr__(name) + super(LuxDataFrame, self).__getattr__(name) self.expire_metadata() self.expire_recs() - return ret_value def _set_axis(self, axis, labels): super(LuxDataFrame, self)._set_axis(axis, labels) self.expire_metadata() @@ -116,10 +105,6 @@ def _update_inplace(self,*args,**kwargs): super(LuxDataFrame, self)._update_inplace(*args,**kwargs) self.expire_metadata() self.expire_recs() - 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 (self._default_pandas_display): @@ -145,12 +130,12 @@ 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 not_int_index_flag = self.index.dtype !='int64' - small_df_flag = len(self)<100 + small_df_flag = len(self)<100 and len(self)>0 self.pre_aggregated = (is_multi_index_flag or not_int_index_flag) and small_df_flag if ("Number of Records" in self.columns): self.pre_aggregated = True very_small_df_flag = len(self)<=10 - if (very_small_df_flag): + if (very_small_df_flag and len(self)>0): self.pre_aggregated = True def set_executor_type(self, exe): if (exe =="SQL"): @@ -160,7 +145,7 @@ def set_executor_type(self, exe): else: import psycopg2 from lux.executor.SQLExecutor import SQLExecutor - self.executor = SQLExecutor + self.executor = SQLExecutor() else: from lux.executor.PandasExecutor import PandasExecutor self.executor = PandasExecutor() @@ -224,12 +209,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` @@ -241,8 +224,8 @@ def _parse_validate_compile_intent(self): from lux.processor.Parser import Parser from lux.processor.Validator import Validator self._intent = Parser.parse(self._intent) - Validator.validate_intent(self._intent,self) self.maintain_metadata() + Validator.validate_intent(self._intent,self) from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) @@ -257,7 +240,6 @@ def copy_intent(self): def set_intent_as_vis(self,vis:Vis): """ Set intent of the dataframe as the Vis - Parameters ---------- vis : Vis @@ -293,92 +275,9 @@ def __repr__(self): def set_SQL_connection(self, connection, t_name): self.SQLconnection = connection self.table_name = t_name - self.compute_SQL_dataset_metadata() self.set_executor_type("SQL") + self.executor.compute_dataset_metadata(self) - def compute_SQL_dataset_metadata(self): - self.get_SQL_attributes() - for attr in list(self.columns): - self[attr] = None - self.data_type_lookup = {} - self.data_type = {} - #####NOTE: since we aren't expecting users to do much data processing with the SQL database, should we just keep this - ##### in the initialization and do it just once - self.compute_SQL_data_type() - self.compute_SQL_stats() - self.data_model_lookup = {} - self.data_model = {} - self.compute_data_model() - - def compute_SQL_stats(self): - # precompute statistics - self.unique_values = {} - self._min_max = {} - - self.get_SQL_unique_values() - #self.get_SQL_cardinality() - for attribute in self.columns: - if self.data_type_lookup[attribute] == 'quantitative': - self._min_max[attribute] = (self[attribute].min(), self[attribute].max()) - - def get_SQL_attributes(self): - if "." in self.table_name: - table_name = self.table_name[self.table_name.index(".")+1:] - else: - table_name = self.table_name - attr_query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '{}'".format(table_name) - attributes = list(pd.read_sql(attr_query, self.SQLconnection)['column_name']) - for attr in attributes: - self[attr] = None - - def get_SQL_cardinality(self): - cardinality = {} - for attr in list(self.columns): - card_query = pd.read_sql("SELECT Count(Distinct({})) FROM {}".format(attr, self.table_name), self.SQLconnection) - cardinality[attr] = list(card_query["count"])[0] - self.cardinality = cardinality - - def get_SQL_unique_values(self): - unique_vals = {} - for attr in list(self.columns): - unique_query = pd.read_sql("SELECT Distinct({}) FROM {}".format(attr, self.table_name), self.SQLconnection) - unique_vals[attr] = list(unique_query[attr]) - self.unique_values = unique_vals - - def compute_SQL_data_type(self): - data_type_lookup = {} - sql_dtypes = {} - self.get_SQL_cardinality() - if "." in self.table_name: - table_name = self.table_name[self.table_name.index(".")+1:] - else: - table_name = self.table_name - #get the data types of the attributes in the SQL table - for attr in list(self.columns): - datatype_query = "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{}' AND COLUMN_NAME = '{}'".format(table_name, attr) - datatype = list(pd.read_sql(datatype_query, self.SQLconnection)['data_type'])[0] - sql_dtypes[attr] = datatype - - data_type = {"quantitative":[], "nominal":[], "temporal":[]} - for attr in list(self.columns): - if str(attr).lower() in ["month", "year"]: - data_type_lookup[attr] = "temporal" - data_type["temporal"].append(attr) - elif sql_dtypes[attr] in ["character", "character varying", "boolean", "uuid", "text"]: - data_type_lookup[attr] = "nominal" - data_type["nominal"].append(attr) - elif sql_dtypes[attr] in ["integer", "real", "smallint", "smallserial", "serial"]: - if self.cardinality[attr] < 13: - data_type_lookup[attr] = "nominal" - data_type["nominal"].append(attr) - else: - data_type_lookup[attr] = "quantitative" - data_type["quantitative"].append(attr) - elif "time" in sql_dtypes[attr] or "date" in sql_dtypes[attr]: - data_type_lookup[attr] = "temporal" - data_type["temporal"].append(attr) - self.data_type_lookup = data_type_lookup - self.data_type = data_type def _append_rec(self,rec_infolist,recommendations:Dict): if (recommendations["collection"] is not None and len(recommendations["collection"])>0): rec_infolist.append(recommendations) @@ -390,17 +289,14 @@ 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.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 rec_df._message = Message() # Add warning message if there exist ID fields - id_fields_str = "" - 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.") + for id_field in rec_df.data_type["id"]: + rec_df._message.append(f"{id_field} 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 @@ -468,7 +364,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 @@ -490,11 +385,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 +406,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 +414,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 @@ -550,7 +435,7 @@ def _repr_html_(self): display(self.display_pandas()) return - if (len(self)<=0): + if (len(self)<=0 and self.executor_type == "Pandas"): warnings.warn("\nLux can not operate on an empty dataframe.\nPlease check your input again.\n",stacklevel=2) display(self.display_pandas()) return @@ -569,9 +454,6 @@ 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() @@ -595,11 +477,11 @@ def on_button_clicked(b): 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) + # 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() @@ -631,9 +513,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 +577,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 @@ -715,4 +597,4 @@ def info(self, *args, **kwargs): def describe(self, *args, **kwargs): self._pandas_only=True self._history.append_event("describe",*args, **kwargs) - return super(LuxDataFrame, self).describe(*args, **kwargs) + return super(LuxDataFrame, self).describe(*args, **kwargs) \ No newline at end of file diff --git a/lux/executor/Executor.py b/lux/executor/Executor.py index f2216131..2b0a3f79 100644 --- a/lux/executor/Executor.py +++ b/lux/executor/Executor.py @@ -1,17 +1,3 @@ -# 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 lux.vis.VisList import VisList from lux.utils import utils class Executor: @@ -52,7 +38,7 @@ def compute_data_model(self): def mapping(self, rmap): group_map = {} - for val in ["quantitative", "id", "nominal", "temporal"]: + for val in ["quantitative", "id", "ordinal", "nominal", "temporal"]: group_map[val] = list(filter(lambda x: rmap[x] == val, rmap)) return group_map diff --git a/lux/executor/SQLExecutor.py b/lux/executor/SQLExecutor.py index 65500d55..b3b1dc20 100644 --- a/lux/executor/SQLExecutor.py +++ b/lux/executor/SQLExecutor.py @@ -1,23 +1,11 @@ -# 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. - import pandas from lux.vis.VisList import VisList from lux.vis.Vis import Vis from lux.core.frame import LuxDataFrame from lux.executor.Executor import Executor from lux.utils import utils +from lux.utils.utils import check_import_lux_widget, check_if_id_like + import math class SQLExecutor(Executor): @@ -34,46 +22,47 @@ def __repr__(self): return f"" @staticmethod - def execute(vislist:VisList, ldf: LuxDataFrame): - import pandas as pd + def execute(view_collection:VisList, ldf: LuxDataFrame): ''' - Given a VisList, fetch the data required to render the vis + Given a VisList, fetch the data required to render the view 1) Apply filters 2) Retreive relevant attribute 3) return a DataFrame with relevant results ''' - for vis in vislist: + for view in view_collection: # Select relevant data based on attribute information attributes = set([]) - for clause in vis._inferred_intent: + for clause in view._inferred_intent: if (clause.attribute): if (clause.attribute=="Record"): attributes.add(clause.attribute) #else: attributes.add(clause.attribute) - if vis.mark not in ["bar", "line", "histogram"]: - where_clause, filterVars = SQLExecutor.execute_filter(vis) + if view.mark not in ["bar", "line", "histogram"]: + where_clause, filterVars = SQLExecutor.execute_filter(view) required_variables = attributes | set(filterVars) required_variables = ",".join(required_variables) - row_count = list(pd.read_sql("SELECT COUNT(*) FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection)['count'])[0] + row_count = list(pandas.read_sql("SELECT COUNT(*) FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection)['count'])[0] if row_count > 10000: query = "SELECT {} FROM {} {} ORDER BY random() LIMIT 10000".format(required_variables, ldf.table_name, where_clause) else: query = "SELECT {} FROM {} {}".format(required_variables, ldf.table_name, where_clause) - data = pd.read_sql(query, ldf.SQLconnection) - vis._vis_data = utils.pandas_to_lux(data) - if (vis.mark =="bar" or vis.mark =="line"): - SQLExecutor.execute_aggregate(vis, ldf) - elif (vis.mark =="histogram"): - SQLExecutor.execute_binning(vis, ldf) + data = pandas.read_sql(query, ldf.SQLconnection) + view._vis_data = utils.pandas_to_lux(data) + if (view.mark =="bar" or view.mark =="line"): + SQLExecutor.execute_aggregate(view, ldf) + elif (view.mark =="histogram"): + SQLExecutor.execute_binning(view, ldf) @staticmethod - def execute_aggregate(vis:Vis, ldf:LuxDataFrame): - import pandas as pd - x_attr = vis.get_attr_by_channel("x")[0] - y_attr = vis.get_attr_by_channel("y")[0] + def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): + x_attr = view.get_attr_by_channel("x")[0] + y_attr = view.get_attr_by_channel("y")[0] + has_color = False groupby_attr ="" measure_attr ="" + if (x_attr.aggregation is None or y_attr.aggregation is None): + return if (y_attr.aggregation!=""): groupby_attr = x_attr measure_attr = y_attr @@ -82,90 +71,144 @@ def execute_aggregate(vis:Vis, ldf:LuxDataFrame): groupby_attr = y_attr measure_attr = x_attr agg_func = x_attr.aggregation - + if (groupby_attr.attribute in ldf.unique_values.keys()): + attr_unique_vals = ldf.unique_values[groupby_attr.attribute] + #checks if color is specified in the Vis + if len(view.get_attr_by_channel("color")) == 1: + color_attr = view.get_attr_by_channel("color")[0] + color_attr_vals = ldf.unique_values[color_attr.attribute] + color_cardinality = len(color_attr_vals) + #NOTE: might want to have a check somewhere to not use categorical variables with greater than some number of categories as a Color variable---------------- + has_color = True + else: + color_cardinality = 1 if (measure_attr!=""): #barchart case, need count data for each group if (measure_attr.attribute=="Record"): - where_clause, filterVars = SQLExecutor.execute_filter(vis) - count_query = "SELECT {}, COUNT({}) FROM {} {} GROUP BY {}".format(groupby_attr.attribute, groupby_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) - vis._vis_data = pd.read_sql(count_query, ldf.SQLconnection) - vis._vis_data = vis.data.rename(columns={"count":"Record"}) - vis._vis_data = utils.pandas_to_lux(vis.data) - + where_clause, filterVars = SQLExecutor.execute_filter(view) + if has_color: + count_query = "SELECT {}, {}, COUNT({}) FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, groupby_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) + view._vis_data = pandas.read_sql(count_query, ldf.SQLconnection) + view._vis_data = view._vis_data.rename(columns={"count":"Record"}) + view._vis_data = utils.pandas_to_lux(view._vis_data) + else: + count_query = "SELECT {}, COUNT({}) FROM {} {} GROUP BY {}".format(groupby_attr.attribute, groupby_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + view._vis_data = pandas.read_sql(count_query, ldf.SQLconnection) + view._vis_data = view._vis_data.rename(columns={"count":"Record"}) + view._vis_data = utils.pandas_to_lux(view._vis_data) + #aggregate barchart case, need aggregate data for each group else: - where_clause, filterVars = SQLExecutor.execute_filter(vis) - if agg_func == "mean": - mean_query = "SELECT {}, AVG({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) - vis._vis_data = pd.read_sql(mean_query, ldf.SQLconnection) - vis._vis_data = utils.pandas_to_lux(vis.data) - if agg_func == "sum": - mean_query = "SELECT {}, SUM({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) - vis._vis_data = pd.read_sql(mean_query, ldf.SQLconnection) - vis._vis_data = utils.pandas_to_lux(vis.data) - if agg_func == "max": - mean_query = "SELECT {}, MAX({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) - vis._vis_data = pd.read_sql(mean_query, ldf.SQLconnection) - vis._vis_data = utils.pandas_to_lux(vis.data) - - #pad empty categories with 0 counts after filter is applied - all_attr_vals = ldf.unique_values[groupby_attr.attribute] - result_vals = list(vis.data[groupby_attr.attribute]) - if (len(result_vals) != len(all_attr_vals)): - # For filtered aggregation that have missing groupby-attribute values, set these aggregated value as 0, since no datapoints - for vals in all_attr_vals: - if (vals not in result_vals): - vis.data.loc[len(vis.data)] = [vals]+[0]*(len(vis.data.columns)-1) + where_clause, filterVars = SQLExecutor.execute_filter(view) + if has_color: + if agg_func == "mean": + mean_query = "SELECT {}, {}, AVG({}) as {} FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) + view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) + view._vis_data = utils.pandas_to_lux(view._vis_data) + if agg_func == "sum": + mean_query = "SELECT {}, {}, SUM({}) as {} FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) + view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) + view._vis_data = utils.pandas_to_lux(view._vis_data) + if agg_func == "max": + mean_query = "SELECT {}, {}, MAX({}) as {} FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) + view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) + view._vis_data = utils.pandas_to_lux(view._vis_data) + else: + if agg_func == "mean": + mean_query = "SELECT {}, AVG({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) + view._vis_data = utils.pandas_to_lux(view._vis_data) + if agg_func == "sum": + mean_query = "SELECT {}, SUM({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) + view._vis_data = utils.pandas_to_lux(view._vis_data) + if agg_func == "max": + mean_query = "SELECT {}, MAX({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) + view._vis_data = utils.pandas_to_lux(view._vis_data) + result_vals = list(view._vis_data[groupby_attr.attribute]) + #create existing group by attribute combinations if color is specified + #this is needed to check what combinations of group_by_attr and color_attr values have a non-zero number of elements in them + if has_color: + res_color_combi_vals = [] + result_color_vals = list(view._vis_data[color_attr.attribute]) + for i in range(0, len(result_vals)): + res_color_combi_vals.append([result_vals[i], result_color_vals[i]]) + # For filtered aggregation that have missing groupby-attribute values, set these aggregated value as 0, since no datapoints + if (isFiltered or has_color and attr_unique_vals): + N_unique_vals = len(attr_unique_vals) + if (len(result_vals) != N_unique_vals*color_cardinality): + columns = view._vis_data.columns + if has_color: + df = pandas.DataFrame({columns[0]: attr_unique_vals*color_cardinality, columns[1]: pandas.Series(color_attr_vals).repeat(N_unique_vals)}) + view._vis_data = view._vis_data.merge(df, on=[columns[0],columns[1]], how='right', suffixes=['', '_right']) + for col in columns[2:]: + view._vis_data[col] = view._vis_data[col].fillna(0) #Triggers __setitem__ + assert len(list(view._vis_data[groupby_attr.attribute])) == N_unique_vals*len(color_attr_vals), f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute, color_attr.attribute}`." + view._vis_data = view._vis_data.iloc[:,:3] # Keep only the three relevant columns not the *_right columns resulting from merge + else: + df = pandas.DataFrame({columns[0]: attr_unique_vals}) + + view._vis_data = view._vis_data.merge(df, on=columns[0], how='right', suffixes=['', '_right']) + + for col in columns[1:]: + view._vis_data[col] = view._vis_data[col].fillna(0) + assert len(list(view._vis_data[groupby_attr.attribute])) == N_unique_vals, f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute}`." + view._vis_data = view._vis_data.sort_values(by=groupby_attr.attribute, ascending=True) + view._vis_data = view._vis_data.reset_index() + view._vis_data = view._vis_data.drop(columns="index") + @staticmethod - def execute_binning(vis:Vis, ldf:LuxDataFrame): + def execute_binning(view:Vis, ldf:LuxDataFrame): import numpy as np - import pandas as pd - bin_attribute = list(filter(lambda x: x.bin_size!=0,vis._inferred_intent))[0] - if not math.isnan(vis.data.min_max[bin_attribute.attribute][0]) and math.isnan(vis.data.min_max[bin_attribute.attribute][1]): - num_bins = bin_attribute.bin_size - attr_min = min(ldf.unique_values[bin_attribute.attribute]) - attr_max = max(ldf.unique_values[bin_attribute.attribute]) - attr_type = type(ldf.unique_values[bin_attribute.attribute][0]) - - #need to calculate the bin edges before querying for the relevant data - bin_width = (attr_max-attr_min)/num_bins - upper_edges = [] - for e in range(1, num_bins): - curr_edge = attr_min + e*bin_width - if attr_type == int: - upper_edges.append(str(math.ceil(curr_edge))) - else: - upper_edges.append(str(curr_edge)) - upper_edges = ",".join(upper_edges) - vis_filter, filter_vars = SQLExecutor.execute_filter(vis) - bin_count_query = "SELECT width_bucket, COUNT(width_bucket) FROM (SELECT width_bucket({}, '{}') FROM {}) as Buckets GROUP BY width_bucket ORDER BY width_bucket".format(bin_attribute.attribute, '{'+upper_edges+'}', ldf.table_name) - bin_count_data = pd.read_sql(bin_count_query, ldf.SQLconnection) - - #counts,binEdges = np.histogram(ldf[bin_attribute.attribute],bins=bin_attribute.bin_size) - #binEdges of size N+1, so need to compute binCenter as the bin location - upper_edges = [float(i) for i in upper_edges.split(",")] - if attr_type == int: - bin_centers = np.array([math.ceil((attr_min+attr_min+bin_width)/2)]) - else: - bin_centers = np.array([(attr_min+attr_min+bin_width)/2]) - bin_centers = np.append(bin_centers, np.mean(np.vstack([upper_edges[0:-1],upper_edges[1:]]), axis=0)) + bin_attribute = list(filter(lambda x: x.bin_size!=0,view._inferred_intent))[0] + + num_bins = bin_attribute.bin_size + attr_min = min(ldf.unique_values[bin_attribute.attribute]) + attr_max = max(ldf.unique_values[bin_attribute.attribute]) + attr_type = type(ldf.unique_values[bin_attribute.attribute][0]) + + #get filters if available + where_clause, filterVars = SQLExecutor.execute_filter(view) + #need to calculate the bin edges before querying for the relevant data + bin_width = (attr_max-attr_min)/num_bins + upper_edges = [] + for e in range(1, num_bins): + curr_edge = attr_min + e*bin_width if attr_type == int: - bin_centers = np.append(bin_centers, math.ceil((upper_edges[len(upper_edges)-1]+attr_max)/2)) + upper_edges.append(str(math.ceil(curr_edge))) else: - bin_centers = np.append(bin_centers, (upper_edges[len(upper_edges)-1]+attr_max)/2) - - if len(bin_centers) > len(bin_count_data): - bucket_lables = bin_count_data['width_bucket'].unique() - for i in range(0,len(bin_centers)): - if i not in bucket_lables: - bin_count_data = bin_count_data.append(pd.DataFrame([[i,0]], columns = bin_count_data.columns)) - vis._vis_data = pd.DataFrame(np.array([bin_centers,list(bin_count_data['count'])]).T,columns=[bin_attribute.attribute, "Number of Records"]) - vis._vis_data = utils.pandas_to_lux(vis.data) + upper_edges.append(str(curr_edge)) + upper_edges = ",".join(upper_edges) + view_filter, filter_vars = SQLExecutor.execute_filter(view) + bin_count_query = "SELECT width_bucket, COUNT(width_bucket) FROM (SELECT width_bucket({}, '{}') FROM {} {}) as Buckets GROUP BY width_bucket ORDER BY width_bucket".format(bin_attribute.attribute, '{'+upper_edges+'}', ldf.table_name, where_clause) + bin_count_data = pandas.read_sql(bin_count_query, ldf.SQLconnection) + + #counts,binEdges = np.histogram(ldf[bin_attribute.attribute],bins=bin_attribute.bin_size) + #binEdges of size N+1, so need to compute binCenter as the bin location + upper_edges = [float(i) for i in upper_edges.split(",")] + if attr_type == int: + bin_centers = np.array([math.ceil((attr_min+attr_min+bin_width)/2)]) + else: + bin_centers = np.array([(attr_min+attr_min+bin_width)/2]) + bin_centers = np.append(bin_centers, np.mean(np.vstack([upper_edges[0:-1],upper_edges[1:]]), axis=0)) + if attr_type == int: + bin_centers = np.append(bin_centers, math.ceil((upper_edges[len(upper_edges)-1]+attr_max)/2)) + else: + bin_centers = np.append(bin_centers, (upper_edges[len(upper_edges)-1]+attr_max)/2) + + if len(bin_centers) > len(bin_count_data): + bucket_lables = bin_count_data['width_bucket'].unique() + for i in range(0,len(bin_centers)): + if i not in bucket_lables: + bin_count_data = bin_count_data.append(pandas.DataFrame([[i,0]], columns = bin_count_data.columns)) + view._vis_data = pandas.DataFrame(np.array([bin_centers,list(bin_count_data['count'])]).T,columns=[bin_attribute.attribute, "Number of Records"]) + view._vis_data = utils.pandas_to_lux(view.data) @staticmethod - #takes in a vis and returns an appropriate SQL WHERE clause that based on the filters specified in the vis's _inferred_intent - def execute_filter(vis:Vis): + #takes in a view and returns an appropriate SQL WHERE clause that based on the filters specified in the view's _inferred_intent + def execute_filter(view:Vis): where_clause = [] - filters = utils.get_filter_specs(vis._inferred_intent) + filters = utils.get_filter_specs(view._inferred_intent) filter_vars = [] if (filters): for f in range(0,len(filters)): @@ -180,4 +223,103 @@ def execute_filter(vis:Vis): return("", []) else: where_clause = " ".join(where_clause) - return(where_clause, filter_vars) \ No newline at end of file + return(where_clause, filter_vars) + + + ####################################################### + ########## Metadata, type, model schema ########### + ####################################################### + + def compute_dataset_metadata(self, ldf:LuxDataFrame): + self.get_SQL_attributes(ldf) + for attr in list(ldf.columns): + ldf[attr] = None + ldf.data_type_lookup = {} + ldf.data_type = {} + #####NOTE: since we aren't expecting users to do much data processing with the SQL database, should we just keep this + ##### in the initialization and do it just once + self.compute_data_type(ldf) + self.compute_stats(ldf) + ldf.data_model_lookup = {} + ldf.data_model = {} + self.compute_data_model(ldf) + + def get_SQL_attributes(self, ldf:LuxDataFrame): + if "." in ldf.table_name: + table_name = ldf.table_name[self.table_name.index(".")+1:] + else: + table_name = ldf.table_name + attr_query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '{}'".format(table_name) + attributes = list(pandas.read_sql(attr_query, ldf.SQLconnection)['column_name']) + for attr in attributes: + ldf[attr] = None + + def compute_stats(self, ldf:LuxDataFrame): + # precompute statistics + ldf.unique_values = {} + ldf._min_max = {} + + self.get_unique_values(ldf) + #ldf.get_cardinality() + for attribute in ldf.columns: + if ldf.data_type_lookup[attribute] == 'quantitative': + min_max_query = pandas.read_sql("SELECT MIN({}) as min, MAX({}) as max FROM {}".format(attribute, attribute, ldf.table_name), ldf.SQLconnection) + ldf._min_max[attribute] = (list(min_max_query["min"])[0], list(min_max_query['max'])[0]) + + def get_cardinality(self, ldf:LuxDataFrame): + cardinality = {} + for attr in list(ldf.columns): + card_query = pandas.read_sql("SELECT Count(Distinct({})) FROM {}".format(attr, ldf.table_name), ldf.SQLconnection) + cardinality[attr] = list(card_query["count"])[0] + ldf.cardinality = cardinality + + def get_unique_values(self, ldf:LuxDataFrame): + unique_vals = {} + for attr in list(ldf.columns): + unique_query = pandas.read_sql("SELECT Distinct({}) FROM {}".format(attr, ldf.table_name), ldf.SQLconnection) + unique_vals[attr] = list(unique_query[attr]) + ldf.unique_values = unique_vals + + def compute_data_type(self, ldf:LuxDataFrame): + data_type_lookup = {} + sql_dtypes = {} + self.get_cardinality(ldf) + if "." in ldf.table_name: + table_name = ldf.table_name[ldf.table_name.index(".")+1:] + else: + table_name = ldf.table_name + #get the data types of the attributes in the SQL table + for attr in list(ldf.columns): + datatype_query = "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{}' AND COLUMN_NAME = '{}'".format(table_name, attr) + datatype = list(pandas.read_sql(datatype_query, ldf.SQLconnection)['data_type'])[0] + sql_dtypes[attr] = datatype + + data_type = {"quantitative":[], "ordinal":[], "nominal":[], "temporal":[], "id":[]} + for attr in list(ldf.columns): + if str(attr).lower() in ["month", "year"]: + data_type_lookup[attr] = "temporal" + data_type["temporal"].append(attr) + elif sql_dtypes[attr] in ["character", "character varying", "boolean", "uuid", "text"]: + data_type_lookup[attr] = "nominal" + data_type["nominal"].append(attr) + elif sql_dtypes[attr] in ["integer", "numeric", "decimal", "bigint", "real", "smallint", "smallserial", "serial"]: + if ldf.cardinality[attr] < 13: + data_type_lookup[attr] = "nominal" + data_type["nominal"].append(attr) + elif check_if_id_like(ldf,attr): + ldf.data_type_lookup[attr] = "id" + else: + data_type_lookup[attr] = "quantitative" + data_type["quantitative"].append(attr) + elif "time" in sql_dtypes[attr] or "date" in sql_dtypes[attr]: + data_type_lookup[attr] = "temporal" + data_type["temporal"].append(attr) + ldf.data_type_lookup = data_type_lookup + ldf.data_type = data_type + + def compute_data_model(self, ldf:LuxDataFrame): + ldf.data_model = { + "measure": ldf.data_type["quantitative"], + "dimension": ldf.data_type["ordinal"] + ldf.data_type["nominal"] + ldf.data_type["temporal"] + } + ldf.data_model_lookup = ldf.executor.reverseMapping(ldf.data_model) \ No newline at end of file diff --git a/lux/processor/Validator.py b/lux/processor/Validator.py index a3262a20..76e931cf 100644 --- a/lux/processor/Validator.py +++ b/lux/processor/Validator.py @@ -32,16 +32,13 @@ def __repr__(self): def validate_intent(intent: List[Clause], ldf:LuxDataFrame) -> None: """ Validates input specifications from the user to find inconsistencies and errors. - Parameters ---------- ldf : lux.core.frame LuxDataFrame with underspecified intent. - Returns ------- None - Raises ------ ValueError From daa9a0de331d3926d978094a22ecada2a6b91d9c Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Fri, 16 Oct 2020 11:10:31 -0700 Subject: [PATCH 002/114] Fix to Validator Uses unique value metadata to verify if a value is valid --- lux/processor/Validator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lux/processor/Validator.py b/lux/processor/Validator.py index 76e931cf..b07da72f 100644 --- a/lux/processor/Validator.py +++ b/lux/processor/Validator.py @@ -18,6 +18,7 @@ from typing import List from lux.utils.date_utils import is_datetime_series,is_datetime_string import warnings +import pandas as pd class Validator: ''' Contains methods for validating lux.Clause objects in the intent. @@ -59,7 +60,7 @@ def validate_clause(clause): if not clause.attribute in list(ldf.columns): warnings.warn(f"The input attribute '{clause.attribute}' does not exist in the DataFrame.") if (clause.value and clause.attribute and clause.filter_op=="="): - series = ldf[clause.attribute] + series = pd.Series(ldf.unique_values[clause.attribute]) if (not is_datetime_series(series)): if isinstance(clause.value, list): vals = clause.value From 804b0dc120fed50f3707e8e7e7921edb8979c726 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sat, 17 Oct 2020 11:53:49 -0700 Subject: [PATCH 003/114] Fix Bug with Widget Rendering frame.py was trying to import luxWidget instead of luxwidget --- lux/core/frame.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 3866e824..4a5a9c7f 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -323,7 +323,6 @@ def maintain_recs(self): 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")) @@ -418,7 +417,6 @@ def _repr_html_(self): from IPython.display import display from IPython.display import clear_output import ipywidgets as widgets - try: if (self._pandas_only): display(self.display_pandas()) @@ -434,7 +432,6 @@ def _repr_html_(self): ) display(self.display_pandas()) return - if (len(self)<=0 and self.executor_type == "Pandas"): warnings.warn("\nLux can not operate on an empty dataframe.\nPlease check your input again.\n",stacklevel=2) display(self.display_pandas()) @@ -444,7 +441,6 @@ def _repr_html_(self): display(self.display_pandas()) return self.maintain_metadata() - if (self._intent!=[] and (not hasattr(self,"_compiled") or not self._compiled)): from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) @@ -513,9 +509,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), From 676f3e074117a441cbea23bde0906fdbadabe27e Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Mon, 19 Oct 2020 15:40:44 -0700 Subject: [PATCH 004/114] Added Number of Observations to MetaData, Fixed Interestingness issue with SQL Executor Some interestingness functions required the number of observations in the data and visualization, so I added these values to the metadata to make the scoring work when using the SQL executor Added tests for SQL executor --- lux/action/univariate.py | 4 +- lux/core/frame.py | 4 +- lux/executor/PandasExecutor.py | 2 + lux/executor/SQLExecutor.py | 15 +++ lux/interestingness/interestingness.py | 8 +- tests/test_sql_executor.py | 174 +++++++++++++++++++++++++ 6 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 tests/test_sql_executor.py diff --git a/lux/action/univariate.py b/lux/action/univariate.py index c4b289b6..fc3c0364 100644 --- a/lux/action/univariate.py +++ b/lux/action/univariate.py @@ -42,7 +42,7 @@ def univariate(ldf, data_type_constraint="quantitative"): intent.extend(filter_specs) recommendation = {"action":"Distribution", "description":"Show univariate histograms of

quantitative

attributes."} - if (len(ldf)<5): # Doesn't make sense to generate a histogram if there is less than 5 datapoints (pre-aggregated) + if (len(ldf)<5 and ldf.executor_type == "Pandas"): # Doesn't make sense to generate a histogram if there is less than 5 datapoints (pre-aggregated) ignore_rec_flag = True elif (data_type_constraint == "nominal"): intent = [lux.Clause("?",data_type="nominal")] @@ -54,7 +54,7 @@ def univariate(ldf, data_type_constraint="quantitative"): intent.extend(filter_specs) recommendation = {"action":"Temporal", "description":"Show trends over

time-related

attributes."} - if (len(ldf)<3): # Doesn't make sense to generate a line chart if there is less than 3 datapoints (pre-aggregated) + if (len(ldf)<3 and ldf.executor_type == "Pandas"): # Doesn't make sense to generate a line chart if there is less than 3 datapoints (pre-aggregated) ignore_rec_flag = True if (ignore_rec_flag): recommendation["collection"] = [] diff --git a/lux/core/frame.py b/lux/core/frame.py index 4a5a9c7f..4dceb2e9 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -16,7 +16,7 @@ class LuxDataFrame(pd.DataFrame): A subclass of pd.DataFrame that supports all dataframe operations while housing other variables and functions for generating visual recommendations. ''' # MUST register here for new properties!! - _metadata = ['_intent','data_type_lookup','data_type', + _metadata = ['_intent','data_type_lookup','data_type', 'num_obs', 'data_model_lookup','data_model','unique_values','cardinality','_rec_info', '_pandas_only', '_min_max','plot_config', '_current_vis','_widget', '_recommendation','_prev','_history'] @@ -40,6 +40,7 @@ def __init__(self,*args, **kw): self._message = Message() self._pandas_only=False # Metadata + self.num_obs = None self.data_type_lookup = None self.data_type = None self.data_model_lookup = None @@ -77,6 +78,7 @@ def expire_recs(self): def expire_metadata(self): # Set metadata as null self._metadata_fresh = False + self.num_obs = None self.data_type_lookup = None self.data_type = None self.data_model_lookup = None diff --git a/lux/executor/PandasExecutor.py b/lux/executor/PandasExecutor.py index 6924886d..cdb01b5f 100644 --- a/lux/executor/PandasExecutor.py +++ b/lux/executor/PandasExecutor.py @@ -389,6 +389,8 @@ def compute_stats(self, ldf:LuxDataFrame): ldf._min_max = {} ldf.cardinality = {} + ldf.num_obs = len(ldf) + for attribute in ldf.columns: if (isinstance(attribute,pd._libs.tslibs.timestamps.Timestamp)): diff --git a/lux/executor/SQLExecutor.py b/lux/executor/SQLExecutor.py index b3b1dc20..bdaa6242 100644 --- a/lux/executor/SQLExecutor.py +++ b/lux/executor/SQLExecutor.py @@ -40,6 +40,9 @@ def execute(view_collection:VisList, ldf: LuxDataFrame): attributes.add(clause.attribute) if view.mark not in ["bar", "line", "histogram"]: where_clause, filterVars = SQLExecutor.execute_filter(view) + + num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) + required_variables = attributes | set(filterVars) required_variables = ",".join(required_variables) row_count = list(pandas.read_sql("SELECT COUNT(*) FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection)['count'])[0] @@ -49,6 +52,7 @@ def execute(view_collection:VisList, ldf: LuxDataFrame): query = "SELECT {} FROM {} {}".format(required_variables, ldf.table_name, where_clause) data = pandas.read_sql(query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(data) + view._vis_data.num_obs = list(num_obs_query['num_obs'])[0] if (view.mark =="bar" or view.mark =="line"): SQLExecutor.execute_aggregate(view, ldf) elif (view.mark =="histogram"): @@ -86,6 +90,8 @@ def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): #barchart case, need count data for each group if (measure_attr.attribute=="Record"): where_clause, filterVars = SQLExecutor.execute_filter(view) + + num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) if has_color: count_query = "SELECT {}, {}, COUNT({}) FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, groupby_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) view._vis_data = pandas.read_sql(count_query, ldf.SQLconnection) @@ -96,9 +102,12 @@ def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): view._vis_data = pandas.read_sql(count_query, ldf.SQLconnection) view._vis_data = view._vis_data.rename(columns={"count":"Record"}) view._vis_data = utils.pandas_to_lux(view._vis_data) + view._vis_data.num_obs = list(num_obs_query['num_obs'])[0] #aggregate barchart case, need aggregate data for each group else: where_clause, filterVars = SQLExecutor.execute_filter(view) + + num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) if has_color: if agg_func == "mean": mean_query = "SELECT {}, {}, AVG({}) as {} FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) @@ -156,6 +165,7 @@ def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): view._vis_data = view._vis_data.sort_values(by=groupby_attr.attribute, ascending=True) view._vis_data = view._vis_data.reset_index() view._vis_data = view._vis_data.drop(columns="index") + view._vis_data.num_obs = list(num_obs_query['num_obs'])[0] @staticmethod def execute_binning(view:Vis, ldf:LuxDataFrame): @@ -169,6 +179,8 @@ def execute_binning(view:Vis, ldf:LuxDataFrame): #get filters if available where_clause, filterVars = SQLExecutor.execute_filter(view) + + num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) #need to calculate the bin edges before querying for the relevant data bin_width = (attr_max-attr_min)/num_bins upper_edges = [] @@ -203,6 +215,7 @@ def execute_binning(view:Vis, ldf:LuxDataFrame): bin_count_data = bin_count_data.append(pandas.DataFrame([[i,0]], columns = bin_count_data.columns)) view._vis_data = pandas.DataFrame(np.array([bin_centers,list(bin_count_data['count'])]).T,columns=[bin_attribute.attribute, "Number of Records"]) view._vis_data = utils.pandas_to_lux(view.data) + view._vis_data.num_obs = list(num_obs_query['num_obs'])[0] @staticmethod #takes in a view and returns an appropriate SQL WHERE clause that based on the filters specified in the view's _inferred_intent @@ -258,6 +271,8 @@ def compute_stats(self, ldf:LuxDataFrame): # precompute statistics ldf.unique_values = {} ldf._min_max = {} + num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {}".format(ldf.table_name), ldf.SQLconnection) + ldf.num_obs = list(num_obs_query['num_obs'])[0] self.get_unique_values(ldf) #ldf.get_cardinality() diff --git a/lux/interestingness/interestingness.py b/lux/interestingness/interestingness.py index cc8c1927..c38df266 100644 --- a/lux/interestingness/interestingness.py +++ b/lux/interestingness/interestingness.py @@ -176,8 +176,12 @@ def deviation_from_overall(vis:Vis, ldf:LuxDataFrame, filter_specs:list, msr_att int Score describing how different the vis is from the overall vis """ - v_filter_size = get_filtered_size(filter_specs, ldf) - v_size = len(vis.data) + if(ldf.executor_type == "Pandas"): + v_filter_size = get_filtered_size(filter_specs, ldf) + v_size = len(vis.data) + else: + v_filter_size = vis._vis_data.num_obs + v_size = ldf.num_obs v_filter = vis.data[msr_attribute] total = v_filter.sum() v_filter = v_filter/total # normalize by total to get ratio diff --git a/tests/test_sql_executor.py b/tests/test_sql_executor.py new file mode 100644 index 00000000..cd1443c9 --- /dev/null +++ b/tests/test_sql_executor.py @@ -0,0 +1,174 @@ +# 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 +from lux.executor.SQLExecutor import SQLExecutor +from lux.vis.Vis import Vis +from lux.vis.VisList import VisList +import psycopg2 + +def test_lazy_execution(): + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + + intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] + vis = Vis(intent) + # Check data field in vis is empty before calling executor + assert vis.data is None + SQLExecutor.execute([vis], sql_df) + assert type(vis.data) == lux.core.frame.LuxDataFrame + +def test_selection(): + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + + sql_df["year"] = pd.to_datetime(sql_df["year"], format='%Y') # change pandas dtype for the column "Year" to datetype + intent = [lux.Clause(attribute = ["horsepower", "weight", "acceleration"]), lux.Clause(attribute ="year")] + vislist = VisList(intent,sql_df) + assert all([type(vis.data) == lux.core.frame.LuxDataFrame for vis in vislist]) + assert all(vislist[2].data.columns == ["year", 'acceleration']) + +def test_aggregation(): + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + + intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] + vis = Vis(intent,sql_df) + result_df = vis.data + assert int(result_df[result_df["origin"]=="USA"]["horsepower"])==119 + + intent = [lux.Clause(attribute ="horsepower", aggregation="sum"), lux.Clause(attribute ="origin")] + vis = Vis(intent,sql_df) + result_df = vis.data + assert int(result_df[result_df["origin"]=="Japan"]["horsepower"])==6307 + + intent = [lux.Clause(attribute ="horsepower", aggregation="max"), lux.Clause(attribute ="origin")] + vis = Vis(intent,sql_df) + result_df = vis.data + assert int(result_df[result_df["origin"]=="Europe"]["horsepower"])==133 + +def test_colored_bar_chart(): + from lux.vis.Vis import Vis + from lux.vis.Vis import Clause + + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + + x_clause = Clause(attribute = "milespergal", channel = "x") + y_clause = Clause(attribute = "origin", channel = "y") + color_clause = Clause(attribute = 'cylinders', channel = "color") + + new_vis = Vis([x_clause, y_clause, color_clause],sql_df) + #make sure dimention of the data is correct + color_cardinality = len(sql_df.unique_values['cylinders']) + group_by_cardinality = len(sql_df.unique_values['origin']) + assert (len(new_vis.data.columns)==3) + assert(len(new_vis.data)==15 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values + + + +def test_colored_line_chart(): + from lux.vis.Vis import Vis + from lux.vis.Vis import Clause + + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + sql_df["year"] = pd.to_datetime(sql_df["year"], format='%Y') # change pandas dtype for the column "Year" to datetype + + x_clause = Clause(attribute = "year", channel = "x") + y_clause = Clause(attribute = "milespergal", channel = "y") + color_clause = Clause(attribute = 'cylinders', channel = "color") + + new_vis = Vis([x_clause, y_clause, color_clause],sql_df) + + #make sure dimention of the data is correct + color_cardinality = len(sql_df.unique_values['cylinders']) + group_by_cardinality = len(sql_df.unique_values['year']) + assert (len(new_vis.data.columns)==3) + assert(len(new_vis.data)==60 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values + + +def test_filter(): + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + sql_df["year"] = pd.to_datetime(sql_df["year"], format='%Y') # change pandas dtype for the column "Year" to datetype + + intent = [lux.Clause(attribute ="horsepower"), lux.Clause(attribute ="year"), lux.Clause(attribute ="origin", filter_op="=", value ="USA")] + vis = Vis(intent,sql_df) + vis._vis_data = sql_df + filter_output = SQLExecutor.execute_filter(vis) + assert filter_output[0] == "WHERE origin = 'USA'" + assert filter_output[1] == ['origin'] + +def test_inequalityfilter(): + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + vis = Vis([lux.Clause(attribute ="horsepower", filter_op=">", value=50), lux.Clause(attribute ="milespergal")]) + vis._vis_data = sql_df + filter_output = SQLExecutor.execute_filter(vis) + assert filter_output[0] == "WHERE horsepower > '50'" + assert filter_output[1] == ['horsepower'] + + intent = [lux.Clause(attribute ="horsepower", filter_op="<=", value=100), lux.Clause(attribute ="milespergal")] + vis = Vis(intent,sql_df) + vis._vis_data = sql_df + filter_output = SQLExecutor.execute_filter(vis) + assert filter_output[0] == "WHERE horsepower <= '100'" + assert filter_output[1] == ['horsepower'] + +def test_binning(): + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + vis = Vis([lux.Clause(attribute ="horsepower")],sql_df) + nbins =list(filter(lambda x: x.bin_size!=0, vis._inferred_intent))[0].bin_size + assert len(vis.data) == nbins + +def test_record(): + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + vis = Vis([lux.Clause(attribute ="cylinders")],sql_df) + assert len(vis.data) == len(sql_df.unique_values["cylinders"]) + +def test_filter_aggregation_fillzero_aligned(): + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + intent = [lux.Clause(attribute="cylinders"), lux.Clause(attribute="milespergal"), lux.Clause("origin=Japan")] + vis = Vis(intent,sql_df) + result = vis.data + assert result[result["cylinders"]==5]["milespergal"].values[0]==0 + assert result[result["cylinders"]==8]["milespergal"].values[0]==0 + +def test_exclude_attribute(): + connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "cars") + intent = [lux.Clause("?", exclude=["name", "year"]), lux.Clause("horsepower")] + vislist = VisList(intent,sql_df) + for vis in vislist: + assert (vis.get_attr_by_channel("x")[0].attribute != "year") + assert (vis.get_attr_by_channel("x")[0].attribute != "name") + assert (vis.get_attr_by_channel("y")[0].attribute != "year") + assert (vis.get_attr_by_channel("y")[0].attribute != "year") From 8763df9892e19e07dd82ab9ef5fc1400faaabc9c Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Mon, 19 Oct 2020 15:43:49 -0700 Subject: [PATCH 005/114] Re-added Licensing Headers --- lux/action/correlation.py | 15 +++++++++++++++ lux/executor/Executor.py | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lux/action/correlation.py b/lux/action/correlation.py index f63a5ba2..e243b2fe 100644 --- a/lux/action/correlation.py +++ b/lux/action/correlation.py @@ -1,3 +1,18 @@ +# 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. + + import lux from lux.interestingness.interestingness import interestingness from lux.processor.Compiler import Compiler diff --git a/lux/executor/Executor.py b/lux/executor/Executor.py index 2b0a3f79..db89817b 100644 --- a/lux/executor/Executor.py +++ b/lux/executor/Executor.py @@ -1,3 +1,18 @@ +# 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 lux.vis.VisList import VisList from lux.utils import utils class Executor: From c2b0b46744978852bc921c71dee9eb544d28db99 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Mon, 19 Oct 2020 15:54:49 -0700 Subject: [PATCH 006/114] Adding Recent frame.py changes --- lux/core/frame.py | 68 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 4dceb2e9..d9a55769 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -1,14 +1,28 @@ +# 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. + + import pandas as pd from lux.core.series import LuxSeries from lux.vis.Clause import Clause from lux.vis.Vis import Vis from lux.vis.VisList import VisList from lux.history.history import History + from lux.utils.date_utils import is_datetime_series from lux.utils.message import Message from lux.utils.utils import check_import_lux_widget -#import for benchmarking -import time from typing import Optional, Dict, Union, List, Callable import warnings class LuxDataFrame(pd.DataFrame): @@ -18,13 +32,14 @@ class LuxDataFrame(pd.DataFrame): # MUST register here for new properties!! _metadata = ['_intent','data_type_lookup','data_type', 'num_obs', '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) @@ -34,6 +49,7 @@ def __init__(self,*args, **kw): self.SQLconnection = "" self.table_name = "" + self._sampled = None self._default_pandas_display = True self._toggle_pandas_display = True self._plot_config = None @@ -74,7 +90,8 @@ def expire_recs(self): self.recommendation = None self.current_vis = None self._widget = None - self._rec_info= None + self._rec_info = None + self._sampled = None def expire_metadata(self): # Set metadata as null self._metadata_fresh = False @@ -96,13 +113,18 @@ def expire_metadata(self): # super(LuxDataFrame, self).__finalize__(other,method,**kwargs) # self.expire_metadata() def __getattr__(self, name): - super(LuxDataFrame, self).__getattr__(name) + ret_value = super(LuxDataFrame, self).__getattr__(name) self.expire_metadata() self.expire_recs() + return ret_value def _set_axis(self, axis, labels): super(LuxDataFrame, self)._set_axis(axis, labels) self.expire_metadata() self.expire_recs() + def _set_item(self, key, value): + super(LuxDataFrame, self)._set_item(key, value) + self.expire_metadata() + self.expire_recs() def _update_inplace(self,*args,**kwargs): super(LuxDataFrame, self)._update_inplace(*args,**kwargs) self.expire_metadata() @@ -291,14 +313,17 @@ 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 rec_df._message = Message() - # Add warning message if there exist ID fields - for id_field in rec_df.data_type["id"]: - rec_df._message.append(f"{id_field} is not visualized since it resembles an ID field.") + # Add warning message if there exist ID fields # Add warning message if there exist ID fields + id_fields_str = "" for id_field in rec_df.data_type["id"]: + 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._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 @@ -386,9 +411,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" @@ -407,6 +434,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( @@ -415,6 +443,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 @@ -452,6 +487,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') + # 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() @@ -475,11 +513,11 @@ def on_button_clicked(b): 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) + 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() From 1b08461cd77a62b14a7ce40e761f7966f09014aa Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Mon, 19 Oct 2020 16:49:48 -0700 Subject: [PATCH 007/114] Adjusted SQL Executor Tests Removed lines that changed Year column type to datetime --- lux/core/frame.py | 6 +++--- tests/test_sql_executor.py | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index d9a55769..4149687d 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -318,8 +318,8 @@ def maintain_recs(self): else: rec_df = self rec_df._message = Message() - # Add warning message if there exist ID fields # Add warning message if there exist ID fields - id_fields_str = "" for id_field in rec_df.data_type["id"]: + # Add warning message if there exist ID fields + id_fields_str = "" 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] @@ -489,7 +489,7 @@ def _repr_html_(self): #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() diff --git a/tests/test_sql_executor.py b/tests/test_sql_executor.py index cd1443c9..79ef2593 100644 --- a/tests/test_sql_executor.py +++ b/tests/test_sql_executor.py @@ -37,7 +37,6 @@ def test_selection(): sql_df = lux.LuxDataFrame() sql_df.set_SQL_connection(connection, "cars") - sql_df["year"] = pd.to_datetime(sql_df["year"], format='%Y') # change pandas dtype for the column "Year" to datetype intent = [lux.Clause(attribute = ["horsepower", "weight", "acceleration"]), lux.Clause(attribute ="year")] vislist = VisList(intent,sql_df) assert all([type(vis.data) == lux.core.frame.LuxDataFrame for vis in vislist]) @@ -91,7 +90,6 @@ def test_colored_line_chart(): connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") sql_df = lux.LuxDataFrame() sql_df.set_SQL_connection(connection, "cars") - sql_df["year"] = pd.to_datetime(sql_df["year"], format='%Y') # change pandas dtype for the column "Year" to datetype x_clause = Clause(attribute = "year", channel = "x") y_clause = Clause(attribute = "milespergal", channel = "y") @@ -110,7 +108,6 @@ def test_filter(): connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") sql_df = lux.LuxDataFrame() sql_df.set_SQL_connection(connection, "cars") - sql_df["year"] = pd.to_datetime(sql_df["year"], format='%Y') # change pandas dtype for the column "Year" to datetype intent = [lux.Clause(attribute ="horsepower"), lux.Clause(attribute ="year"), lux.Clause(attribute ="origin", filter_op="=", value ="USA")] vis = Vis(intent,sql_df) From 38c5e7e435cdb170c1cc952ec06d69eb9399c8d0 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Wed, 21 Oct 2020 20:13:37 -0700 Subject: [PATCH 008/114] Update Frame with new Action Registering --- lux/core/frame.py | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 4149687d..ca5cf316 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -342,25 +342,31 @@ 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 == {}: + # 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") + 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) + + # 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) + lux.update_actions["flag"] = False + + # Store _rec_info into a more user-friendly dictionary form rec_df.recommendation = {} From 14d2f909821b31543acc95eb2e662ba76abc9550 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Wed, 21 Oct 2020 20:16:06 -0700 Subject: [PATCH 009/114] Resolving Conflicts in frame.py --- lux/core/frame.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index ca5cf316..5340387f 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -366,8 +366,6 @@ def maintain_recs(self): 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 = {} for rec_info in rec_infolist: @@ -396,6 +394,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 d783b4cb2722fe2754df0756e72b8e433885e00d Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Wed, 21 Oct 2020 20:21:28 -0700 Subject: [PATCH 010/114] Commenting out local SQL Executor tests SQL Executor tests interfering with travis build, commenting out for now --- lux/core/frame.py | 2 +- tests/test_sql_executor.py | 316 ++++++++++++++++++------------------- 2 files changed, 159 insertions(+), 159 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 5340387f..8c1e094d 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -365,7 +365,7 @@ def maintain_recs(self): 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 = {} for rec_info in rec_infolist: diff --git a/tests/test_sql_executor.py b/tests/test_sql_executor.py index 79ef2593..9932e815 100644 --- a/tests/test_sql_executor.py +++ b/tests/test_sql_executor.py @@ -1,171 +1,171 @@ -# 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 -from lux.executor.SQLExecutor import SQLExecutor -from lux.vis.Vis import Vis -from lux.vis.VisList import VisList -import psycopg2 - -def test_lazy_execution(): - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - - intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] - vis = Vis(intent) - # Check data field in vis is empty before calling executor - assert vis.data is None - SQLExecutor.execute([vis], sql_df) - assert type(vis.data) == lux.core.frame.LuxDataFrame +# # 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 +# from lux.executor.SQLExecutor import SQLExecutor +# from lux.vis.Vis import Vis +# from lux.vis.VisList import VisList +# import psycopg2 + +# def test_lazy_execution(): +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") + +# intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] +# vis = Vis(intent) +# # Check data field in vis is empty before calling executor +# assert vis.data is None +# SQLExecutor.execute([vis], sql_df) +# assert type(vis.data) == lux.core.frame.LuxDataFrame -def test_selection(): - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - - intent = [lux.Clause(attribute = ["horsepower", "weight", "acceleration"]), lux.Clause(attribute ="year")] - vislist = VisList(intent,sql_df) - assert all([type(vis.data) == lux.core.frame.LuxDataFrame for vis in vislist]) - assert all(vislist[2].data.columns == ["year", 'acceleration']) - -def test_aggregation(): - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - - intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] - vis = Vis(intent,sql_df) - result_df = vis.data - assert int(result_df[result_df["origin"]=="USA"]["horsepower"])==119 - - intent = [lux.Clause(attribute ="horsepower", aggregation="sum"), lux.Clause(attribute ="origin")] - vis = Vis(intent,sql_df) - result_df = vis.data - assert int(result_df[result_df["origin"]=="Japan"]["horsepower"])==6307 - - intent = [lux.Clause(attribute ="horsepower", aggregation="max"), lux.Clause(attribute ="origin")] - vis = Vis(intent,sql_df) - result_df = vis.data - assert int(result_df[result_df["origin"]=="Europe"]["horsepower"])==133 - -def test_colored_bar_chart(): - from lux.vis.Vis import Vis - from lux.vis.Vis import Clause - - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - - x_clause = Clause(attribute = "milespergal", channel = "x") - y_clause = Clause(attribute = "origin", channel = "y") - color_clause = Clause(attribute = 'cylinders', channel = "color") - - new_vis = Vis([x_clause, y_clause, color_clause],sql_df) - #make sure dimention of the data is correct - color_cardinality = len(sql_df.unique_values['cylinders']) - group_by_cardinality = len(sql_df.unique_values['origin']) - assert (len(new_vis.data.columns)==3) - assert(len(new_vis.data)==15 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values +# def test_selection(): +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") + +# intent = [lux.Clause(attribute = ["horsepower", "weight", "acceleration"]), lux.Clause(attribute ="year")] +# vislist = VisList(intent,sql_df) +# assert all([type(vis.data) == lux.core.frame.LuxDataFrame for vis in vislist]) +# assert all(vislist[2].data.columns == ["year", 'acceleration']) + +# def test_aggregation(): +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") + +# intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] +# vis = Vis(intent,sql_df) +# result_df = vis.data +# assert int(result_df[result_df["origin"]=="USA"]["horsepower"])==119 + +# intent = [lux.Clause(attribute ="horsepower", aggregation="sum"), lux.Clause(attribute ="origin")] +# vis = Vis(intent,sql_df) +# result_df = vis.data +# assert int(result_df[result_df["origin"]=="Japan"]["horsepower"])==6307 + +# intent = [lux.Clause(attribute ="horsepower", aggregation="max"), lux.Clause(attribute ="origin")] +# vis = Vis(intent,sql_df) +# result_df = vis.data +# assert int(result_df[result_df["origin"]=="Europe"]["horsepower"])==133 + +# def test_colored_bar_chart(): +# from lux.vis.Vis import Vis +# from lux.vis.Vis import Clause + +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") + +# x_clause = Clause(attribute = "milespergal", channel = "x") +# y_clause = Clause(attribute = "origin", channel = "y") +# color_clause = Clause(attribute = 'cylinders', channel = "color") + +# new_vis = Vis([x_clause, y_clause, color_clause],sql_df) +# #make sure dimention of the data is correct +# color_cardinality = len(sql_df.unique_values['cylinders']) +# group_by_cardinality = len(sql_df.unique_values['origin']) +# assert (len(new_vis.data.columns)==3) +# assert(len(new_vis.data)==15 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values -def test_colored_line_chart(): - from lux.vis.Vis import Vis - from lux.vis.Vis import Clause +# def test_colored_line_chart(): +# from lux.vis.Vis import Vis +# from lux.vis.Vis import Clause - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") - x_clause = Clause(attribute = "year", channel = "x") - y_clause = Clause(attribute = "milespergal", channel = "y") - color_clause = Clause(attribute = 'cylinders', channel = "color") +# x_clause = Clause(attribute = "year", channel = "x") +# y_clause = Clause(attribute = "milespergal", channel = "y") +# color_clause = Clause(attribute = 'cylinders', channel = "color") - new_vis = Vis([x_clause, y_clause, color_clause],sql_df) +# new_vis = Vis([x_clause, y_clause, color_clause],sql_df) - #make sure dimention of the data is correct - color_cardinality = len(sql_df.unique_values['cylinders']) - group_by_cardinality = len(sql_df.unique_values['year']) - assert (len(new_vis.data.columns)==3) - assert(len(new_vis.data)==60 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values +# #make sure dimention of the data is correct +# color_cardinality = len(sql_df.unique_values['cylinders']) +# group_by_cardinality = len(sql_df.unique_values['year']) +# assert (len(new_vis.data.columns)==3) +# assert(len(new_vis.data)==60 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values -def test_filter(): - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - - intent = [lux.Clause(attribute ="horsepower"), lux.Clause(attribute ="year"), lux.Clause(attribute ="origin", filter_op="=", value ="USA")] - vis = Vis(intent,sql_df) - vis._vis_data = sql_df - filter_output = SQLExecutor.execute_filter(vis) - assert filter_output[0] == "WHERE origin = 'USA'" - assert filter_output[1] == ['origin'] - -def test_inequalityfilter(): - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - vis = Vis([lux.Clause(attribute ="horsepower", filter_op=">", value=50), lux.Clause(attribute ="milespergal")]) - vis._vis_data = sql_df - filter_output = SQLExecutor.execute_filter(vis) - assert filter_output[0] == "WHERE horsepower > '50'" - assert filter_output[1] == ['horsepower'] +# def test_filter(): +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") + +# intent = [lux.Clause(attribute ="horsepower"), lux.Clause(attribute ="year"), lux.Clause(attribute ="origin", filter_op="=", value ="USA")] +# vis = Vis(intent,sql_df) +# vis._vis_data = sql_df +# filter_output = SQLExecutor.execute_filter(vis) +# assert filter_output[0] == "WHERE origin = 'USA'" +# assert filter_output[1] == ['origin'] + +# def test_inequalityfilter(): +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") +# vis = Vis([lux.Clause(attribute ="horsepower", filter_op=">", value=50), lux.Clause(attribute ="milespergal")]) +# vis._vis_data = sql_df +# filter_output = SQLExecutor.execute_filter(vis) +# assert filter_output[0] == "WHERE horsepower > '50'" +# assert filter_output[1] == ['horsepower'] - intent = [lux.Clause(attribute ="horsepower", filter_op="<=", value=100), lux.Clause(attribute ="milespergal")] - vis = Vis(intent,sql_df) - vis._vis_data = sql_df - filter_output = SQLExecutor.execute_filter(vis) - assert filter_output[0] == "WHERE horsepower <= '100'" - assert filter_output[1] == ['horsepower'] +# intent = [lux.Clause(attribute ="horsepower", filter_op="<=", value=100), lux.Clause(attribute ="milespergal")] +# vis = Vis(intent,sql_df) +# vis._vis_data = sql_df +# filter_output = SQLExecutor.execute_filter(vis) +# assert filter_output[0] == "WHERE horsepower <= '100'" +# assert filter_output[1] == ['horsepower'] -def test_binning(): - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - vis = Vis([lux.Clause(attribute ="horsepower")],sql_df) - nbins =list(filter(lambda x: x.bin_size!=0, vis._inferred_intent))[0].bin_size - assert len(vis.data) == nbins - -def test_record(): - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - vis = Vis([lux.Clause(attribute ="cylinders")],sql_df) - assert len(vis.data) == len(sql_df.unique_values["cylinders"]) +# def test_binning(): +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") +# vis = Vis([lux.Clause(attribute ="horsepower")],sql_df) +# nbins =list(filter(lambda x: x.bin_size!=0, vis._inferred_intent))[0].bin_size +# assert len(vis.data) == nbins + +# def test_record(): +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") +# vis = Vis([lux.Clause(attribute ="cylinders")],sql_df) +# assert len(vis.data) == len(sql_df.unique_values["cylinders"]) -def test_filter_aggregation_fillzero_aligned(): - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - intent = [lux.Clause(attribute="cylinders"), lux.Clause(attribute="milespergal"), lux.Clause("origin=Japan")] - vis = Vis(intent,sql_df) - result = vis.data - assert result[result["cylinders"]==5]["milespergal"].values[0]==0 - assert result[result["cylinders"]==8]["milespergal"].values[0]==0 - -def test_exclude_attribute(): - connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "cars") - intent = [lux.Clause("?", exclude=["name", "year"]), lux.Clause("horsepower")] - vislist = VisList(intent,sql_df) - for vis in vislist: - assert (vis.get_attr_by_channel("x")[0].attribute != "year") - assert (vis.get_attr_by_channel("x")[0].attribute != "name") - assert (vis.get_attr_by_channel("y")[0].attribute != "year") - assert (vis.get_attr_by_channel("y")[0].attribute != "year") +# def test_filter_aggregation_fillzero_aligned(): +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") +# intent = [lux.Clause(attribute="cylinders"), lux.Clause(attribute="milespergal"), lux.Clause("origin=Japan")] +# vis = Vis(intent,sql_df) +# result = vis.data +# assert result[result["cylinders"]==5]["milespergal"].values[0]==0 +# assert result[result["cylinders"]==8]["milespergal"].values[0]==0 + +# def test_exclude_attribute(): +# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") +# sql_df = lux.LuxDataFrame() +# sql_df.set_SQL_connection(connection, "cars") +# intent = [lux.Clause("?", exclude=["name", "year"]), lux.Clause("horsepower")] +# vislist = VisList(intent,sql_df) +# for vis in vislist: +# assert (vis.get_attr_by_channel("x")[0].attribute != "year") +# assert (vis.get_attr_by_channel("x")[0].attribute != "name") +# assert (vis.get_attr_by_channel("y")[0].attribute != "year") +# assert (vis.get_attr_by_channel("y")[0].attribute != "year") From 8f0e6438d99a68ed3c2462aecb0b500cf01e0bfc Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Thu, 22 Oct 2020 14:57:18 +0800 Subject: [PATCH 011/114] Update correlation.py --- lux/action/correlation.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/lux/action/correlation.py b/lux/action/correlation.py index e243b2fe..9cf4d081 100644 --- a/lux/action/correlation.py +++ b/lux/action/correlation.py @@ -1,16 +1,16 @@ -# 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. +# 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. import lux @@ -18,8 +18,6 @@ from lux.processor.Compiler import Compiler from lux.core.frame import LuxDataFrame from lux.vis.VisList import VisList -# for benchmarking -import time from lux.utils import utils @@ -27,12 +25,15 @@ def correlation(ldf: LuxDataFrame, ignore_transpose: bool = True): ''' Generates bivariate visualizations that represent all pairwise relationships in the data. + Parameters ---------- ldf : LuxDataFrame LuxDataFrame with underspecified intent. + ignore_transpose: bool Boolean flag to ignore pairs of attributes whose transpose are already computed (i.e., {X,Y} will be ignored if {Y,X} is already computed) + Returns ------- recommendations : Dict[str,obj] @@ -78,4 +79,4 @@ def check_transpose_not_computed(vlist: VisList, a: str, b: str): if (len(transpose_exist) > 0): return transpose_exist[0].score == -1 else: - return False \ No newline at end of file + return False From d365d5285eae7a46490ab70451081e5f27fb6da8 Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Thu, 22 Oct 2020 15:02:29 +0800 Subject: [PATCH 012/114] Update frame.py --- lux/core/frame.py | 58 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 0a085ed7..58d91ccb 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -1,17 +1,16 @@ -# 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. - +# 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. import pandas as pd from lux.core.series import LuxSeries @@ -23,7 +22,7 @@ from lux.utils.date_utils import is_datetime_series from lux.utils.message import Message from lux.utils.utils import check_import_lux_widget -from typing import Optional, Dict, Union, List, Callable +from typing import Dict, Union, List, Callable import warnings import lux class LuxDataFrame(pd.DataFrame): @@ -109,10 +108,6 @@ def expire_metadata(self): ##################### ## Override Pandas ## ##################### - # def __finalize__(self,other, method: Optional[str] = None, **kwargs): - # print ("lux finalize") - # super(LuxDataFrame, self).__finalize__(other,method,**kwargs) - # self.expire_metadata() def __getattr__(self, name): ret_value = super(LuxDataFrame, self).__getattr__(name) self.expire_metadata() @@ -234,10 +229,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` @@ -249,8 +246,8 @@ def _parse_validate_compile_intent(self): from lux.processor.Parser import Parser from lux.processor.Validator import Validator self._intent = Parser.parse(self._intent) - self.maintain_metadata() Validator.validate_intent(self._intent,self) + self.maintain_metadata() from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) @@ -265,6 +262,7 @@ def copy_intent(self): def set_intent_as_vis(self,vis:Vis): """ Set intent of the dataframe as the Vis + Parameters ---------- vis : Vis @@ -424,7 +422,7 @@ def exported(self) -> Union[Dict[str,VisList], VisList]: exported_vis_lst = self._widget._exportedVisIdxs exported_vis = [] if (exported_vis_lst=={}): - if self._saved_export: + if self._saved_export: return self._saved_export warnings.warn( "\nNo visualization selected to export.\n" @@ -453,17 +451,18 @@ 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) + 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 import ipywidgets as widgets + try: if (self._pandas_only): display(self.display_pandas()) @@ -488,6 +487,7 @@ def _repr_html_(self): display(self.display_pandas()) return self.maintain_metadata() + if (self._intent!=[] and (not hasattr(self,"_compiled") or not self._compiled)): from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) @@ -497,7 +497,7 @@ 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) + #Observers(callback_function, listen_to_this_variable) self._widget.observe(self.removeDeletedRecs, names='deletedIndices') if len(self.recommendation) > 0: @@ -628,7 +628,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 @@ -648,4 +648,4 @@ def info(self, *args, **kwargs): def describe(self, *args, **kwargs): self._pandas_only=True self._history.append_event("describe",*args, **kwargs) - return super(LuxDataFrame, self).describe(*args, **kwargs) \ No newline at end of file + return super(LuxDataFrame, self).describe(*args, **kwargs) From 74ed59c0c0d7bb791777531838538147b8a2ffbc Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Thu, 22 Oct 2020 18:38:34 +0800 Subject: [PATCH 013/114] bugfix: "number of remaining bars" text overcounts for colored bar charts * update number of bars calculation to account for when len(data) double counts --- lux/vislib/altair/BarChart.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lux/vislib/altair/BarChart.py b/lux/vislib/altair/BarChart.py index 540c42ce..8cbe9ab7 100644 --- a/lux/vislib/altair/BarChart.py +++ b/lux/vislib/altair/BarChart.py @@ -38,6 +38,7 @@ def initialize_chart(self): if (x_attr.data_model == "measure"): agg_title = get_agg_title(x_attr) measure_attr = x_attr.attribute + bar_attr = y_attr.attribute y_attr_field = alt.Y(y_attr.attribute, type= y_attr.data_type, axis=alt.Axis(labelOverlap=True)) x_attr_field = alt.X(x_attr.attribute, type= x_attr.data_type, title=agg_title) y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', axis=alt.Axis(labelOverlap=True))" @@ -49,6 +50,7 @@ def initialize_chart(self): else: agg_title = get_agg_title(y_attr) measure_attr = y_attr.attribute + bar_attr = x_attr.attribute x_attr_field = alt.X(x_attr.attribute, type = x_attr.data_type,axis=alt.Axis(labelOverlap=True)) y_attr_field = alt.Y(y_attr.attribute,type=y_attr.data_type,title=agg_title) x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True))" @@ -58,8 +60,9 @@ def initialize_chart(self): x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True),sort='-y')" k=10 self._topkcode = "" - if len(self.data)>k: # Truncating to only top k - remaining_bars = len(self.data)-k + n_bars = len(self.data[bar_attr].unique()) + if n_bars>k: # Truncating to only top k + remaining_bars = n_bars-k self.data = self.data.nlargest(k,measure_attr) self.text = alt.Chart(self.data).mark_text( x=155, From 7da2992395e08c5f68cfb0cbddb6044d8a32b78b Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Thu, 22 Oct 2020 08:57:03 -0700 Subject: [PATCH 014/114] Fixing Code Format --- lux/action/correlation.py | 4 ++-- lux/action/univariate.py | 3 +++ lux/core/frame.py | 12 ++++++------ lux/processor/Validator.py | 3 +++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lux/action/correlation.py b/lux/action/correlation.py index 9cf4d081..f925adad 100644 --- a/lux/action/correlation.py +++ b/lux/action/correlation.py @@ -25,12 +25,12 @@ def correlation(ldf: LuxDataFrame, ignore_transpose: bool = True): ''' Generates bivariate visualizations that represent all pairwise relationships in the data. - + Parameters ---------- ldf : LuxDataFrame LuxDataFrame with underspecified intent. - + ignore_transpose: bool Boolean flag to ignore pairs of attributes whose transpose are already computed (i.e., {X,Y} will be ignored if {Y,X} is already computed) diff --git a/lux/action/univariate.py b/lux/action/univariate.py index 9db389a5..5ab1b863 100644 --- a/lux/action/univariate.py +++ b/lux/action/univariate.py @@ -19,12 +19,15 @@ def univariate(ldf, *args): ''' Generates bar chart distributions of different attributes in the dataframe. + Parameters ---------- ldf : lux.core.frame LuxDataFrame with underspecified intent. + data_type_constraint: str Controls the type of distribution chart that will be rendered. + Returns ------- recommendations : Dict[str,obj] diff --git a/lux/core/frame.py b/lux/core/frame.py index 58d91ccb..eedfc3ad 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -229,12 +229,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` @@ -250,7 +250,7 @@ def _parse_validate_compile_intent(self): self.maintain_metadata() from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) - + def copy_intent(self): #creates a true copy of the dataframe's intent output = [] @@ -262,7 +262,7 @@ def copy_intent(self): def set_intent_as_vis(self,vis:Vis): """ Set intent of the dataframe as the Vis - + Parameters ---------- vis : Vis @@ -323,8 +323,8 @@ def maintain_recs(self): # Add warning message if there exist ID fields id_fields_str = "" 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] + 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._prev = None # reset _prev diff --git a/lux/processor/Validator.py b/lux/processor/Validator.py index b07da72f..08968cf3 100644 --- a/lux/processor/Validator.py +++ b/lux/processor/Validator.py @@ -33,13 +33,16 @@ def __repr__(self): def validate_intent(intent: List[Clause], ldf:LuxDataFrame) -> None: """ Validates input specifications from the user to find inconsistencies and errors. + Parameters ---------- ldf : lux.core.frame LuxDataFrame with underspecified intent. + Returns ------- None + Raises ------ ValueError From f1b7c8b56ecbeb29a474e7f81ede15483df123dd Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Thu, 22 Oct 2020 09:02:48 -0700 Subject: [PATCH 015/114] Cleaning up Pandas Executor imports --- lux/action/correlation.py | 1 - lux/core/frame.py | 1 - lux/executor/PandasExecutor.py | 2 -- 3 files changed, 4 deletions(-) diff --git a/lux/action/correlation.py b/lux/action/correlation.py index f925adad..ab5e73d0 100644 --- a/lux/action/correlation.py +++ b/lux/action/correlation.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - import lux from lux.interestingness.interestingness import interestingness from lux.processor.Compiler import Compiler diff --git a/lux/core/frame.py b/lux/core/frame.py index eedfc3ad..ab440ef7 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -18,7 +18,6 @@ from lux.vis.Vis import Vis from lux.vis.VisList import VisList from lux.history.history import History - from lux.utils.date_utils import is_datetime_series from lux.utils.message import Message from lux.utils.utils import check_import_lux_widget diff --git a/lux/executor/PandasExecutor.py b/lux/executor/PandasExecutor.py index cdb01b5f..6a31debe 100644 --- a/lux/executor/PandasExecutor.py +++ b/lux/executor/PandasExecutor.py @@ -18,7 +18,6 @@ from lux.core.frame import LuxDataFrame from lux.executor.Executor import Executor from lux.utils import utils -from lux.utils.date_utils import is_datetime_series from lux.utils.utils import check_import_lux_widget, check_if_id_like from lux.utils.date_utils import is_datetime_series import warnings @@ -388,7 +387,6 @@ def compute_stats(self, ldf:LuxDataFrame): ldf.unique_values = {} ldf._min_max = {} ldf.cardinality = {} - ldf.num_obs = len(ldf) for attribute in ldf.columns: From d97f0e40b0dc4f6b107b6a780d61ed3f51f694a9 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Thu, 22 Oct 2020 09:19:10 -0700 Subject: [PATCH 016/114] Fix Validation Bug Issue where validator was relying on metadata which was not yet generated, moved metadata calculation before validation step in frame.py --- lux/core/frame.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index ab440ef7..74aaa46e 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -245,8 +245,8 @@ def _parse_validate_compile_intent(self): from lux.processor.Parser import Parser from lux.processor.Validator import Validator self._intent = Parser.parse(self._intent) - Validator.validate_intent(self._intent,self) self.maintain_metadata() + Validator.validate_intent(self._intent,self) from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) @@ -486,7 +486,7 @@ def _repr_html_(self): display(self.display_pandas()) return self.maintain_metadata() - + if (self._intent!=[] and (not hasattr(self,"_compiled") or not self._compiled)): from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) From 582b370a1635a391d026cb213ebea5073fd3538b Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Thu, 22 Oct 2020 20:00:33 -0700 Subject: [PATCH 017/114] Changed metadata variable name Renamed num_obs to length, removed ordinal variable from Executor mapping function --- lux/core/frame.py | 6 ++-- lux/executor/Executor.py | 2 +- lux/executor/PandasExecutor.py | 2 +- lux/executor/SQLExecutor.py | 63 ++++++++++++++++++++++++++++------ 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 74aaa46e..86b46c86 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -29,7 +29,7 @@ class LuxDataFrame(pd.DataFrame): A subclass of pd.DataFrame that supports all dataframe operations while housing other variables and functions for generating visual recommendations. ''' # MUST register here for new properties!! - _metadata = ['_intent','data_type_lookup','data_type', 'num_obs', + _metadata = ['_intent','data_type_lookup','data_type', 'length', 'data_model_lookup','data_model','unique_values','cardinality','_rec_info', '_pandas_only', '_min_max','plot_config', '_current_vis','_widget', '_recommendation','_prev','_history', '_saved_export'] @@ -55,7 +55,7 @@ def __init__(self,*args, **kw): self._message = Message() self._pandas_only=False # Metadata - self.num_obs = None + self.length = None self.data_type_lookup = None self.data_type = None self.data_model_lookup = None @@ -94,7 +94,7 @@ def expire_recs(self): def expire_metadata(self): # Set metadata as null self._metadata_fresh = False - self.num_obs = None + self.length = None self.data_type_lookup = None self.data_type = None self.data_model_lookup = None diff --git a/lux/executor/Executor.py b/lux/executor/Executor.py index db89817b..740335fa 100644 --- a/lux/executor/Executor.py +++ b/lux/executor/Executor.py @@ -53,7 +53,7 @@ def compute_data_model(self): def mapping(self, rmap): group_map = {} - for val in ["quantitative", "id", "ordinal", "nominal", "temporal"]: + for val in ["quantitative", "id", "nominal", "temporal"]: group_map[val] = list(filter(lambda x: rmap[x] == val, rmap)) return group_map diff --git a/lux/executor/PandasExecutor.py b/lux/executor/PandasExecutor.py index 6a31debe..1def9b04 100644 --- a/lux/executor/PandasExecutor.py +++ b/lux/executor/PandasExecutor.py @@ -387,7 +387,7 @@ def compute_stats(self, ldf:LuxDataFrame): ldf.unique_values = {} ldf._min_max = {} ldf.cardinality = {} - ldf.num_obs = len(ldf) + ldf.length = len(ldf) for attribute in ldf.columns: diff --git a/lux/executor/SQLExecutor.py b/lux/executor/SQLExecutor.py index bdaa6242..eee5d3cd 100644 --- a/lux/executor/SQLExecutor.py +++ b/lux/executor/SQLExecutor.py @@ -41,7 +41,7 @@ def execute(view_collection:VisList, ldf: LuxDataFrame): if view.mark not in ["bar", "line", "histogram"]: where_clause, filterVars = SQLExecutor.execute_filter(view) - num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) + length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) required_variables = attributes | set(filterVars) required_variables = ",".join(required_variables) @@ -52,7 +52,7 @@ def execute(view_collection:VisList, ldf: LuxDataFrame): query = "SELECT {} FROM {} {}".format(required_variables, ldf.table_name, where_clause) data = pandas.read_sql(query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(data) - view._vis_data.num_obs = list(num_obs_query['num_obs'])[0] + view._vis_data.length = list(length_query['length'])[0] if (view.mark =="bar" or view.mark =="line"): SQLExecutor.execute_aggregate(view, ldf) elif (view.mark =="histogram"): @@ -60,6 +60,22 @@ def execute(view_collection:VisList, ldf: LuxDataFrame): @staticmethod def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): + ''' + Aggregate data points on an axis for bar or line charts + + Parameters + ---------- + vis: lux.Vis + lux.Vis object that represents a visualization + ldf : lux.core.frame + LuxDataFrame with specified intent. + isFiltered: boolean + boolean that represents whether a vis has had a filter applied + + Returns + ------- + None + ''' x_attr = view.get_attr_by_channel("x")[0] y_attr = view.get_attr_by_channel("y")[0] has_color = False @@ -91,7 +107,7 @@ def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): if (measure_attr.attribute=="Record"): where_clause, filterVars = SQLExecutor.execute_filter(view) - num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) + length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) if has_color: count_query = "SELECT {}, {}, COUNT({}) FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, groupby_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) view._vis_data = pandas.read_sql(count_query, ldf.SQLconnection) @@ -102,12 +118,12 @@ def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): view._vis_data = pandas.read_sql(count_query, ldf.SQLconnection) view._vis_data = view._vis_data.rename(columns={"count":"Record"}) view._vis_data = utils.pandas_to_lux(view._vis_data) - view._vis_data.num_obs = list(num_obs_query['num_obs'])[0] + view._vis_data.length = list(length_query['length'])[0] #aggregate barchart case, need aggregate data for each group else: where_clause, filterVars = SQLExecutor.execute_filter(view) - num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) + length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) if has_color: if agg_func == "mean": mean_query = "SELECT {}, {}, AVG({}) as {} FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) @@ -165,10 +181,24 @@ def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): view._vis_data = view._vis_data.sort_values(by=groupby_attr.attribute, ascending=True) view._vis_data = view._vis_data.reset_index() view._vis_data = view._vis_data.drop(columns="index") - view._vis_data.num_obs = list(num_obs_query['num_obs'])[0] + view._vis_data.length = list(length_query['length'])[0] @staticmethod def execute_binning(view:Vis, ldf:LuxDataFrame): + ''' + Binning of data points for generating histograms + + Parameters + ---------- + vis: lux.Vis + lux.Vis object that represents a visualization + ldf : lux.core.frame + LuxDataFrame with specified intent. + + Returns + ------- + None + ''' import numpy as np bin_attribute = list(filter(lambda x: x.bin_size!=0,view._inferred_intent))[0] @@ -180,7 +210,7 @@ def execute_binning(view:Vis, ldf:LuxDataFrame): #get filters if available where_clause, filterVars = SQLExecutor.execute_filter(view) - num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) + length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) #need to calculate the bin edges before querying for the relevant data bin_width = (attr_max-attr_min)/num_bins upper_edges = [] @@ -215,11 +245,24 @@ def execute_binning(view:Vis, ldf:LuxDataFrame): bin_count_data = bin_count_data.append(pandas.DataFrame([[i,0]], columns = bin_count_data.columns)) view._vis_data = pandas.DataFrame(np.array([bin_centers,list(bin_count_data['count'])]).T,columns=[bin_attribute.attribute, "Number of Records"]) view._vis_data = utils.pandas_to_lux(view.data) - view._vis_data.num_obs = list(num_obs_query['num_obs'])[0] + view._vis_data.length = list(length_query['length'])[0] @staticmethod #takes in a view and returns an appropriate SQL WHERE clause that based on the filters specified in the view's _inferred_intent def execute_filter(view:Vis): + """ + Helper function for converting a filter specification to a SQL where clause + + Parameters + ---------- + vis: lux.Vis + lux.Vis object that represents a visualization + + Returns + ------- + list: list of lists + List containing the list of components to be used in the SQL where clause, and the list of variables used in this clause + """ where_clause = [] filters = utils.get_filter_specs(view._inferred_intent) filter_vars = [] @@ -271,8 +314,8 @@ def compute_stats(self, ldf:LuxDataFrame): # precompute statistics ldf.unique_values = {} ldf._min_max = {} - num_obs_query = pandas.read_sql("SELECT COUNT(*) as num_obs FROM {}".format(ldf.table_name), ldf.SQLconnection) - ldf.num_obs = list(num_obs_query['num_obs'])[0] + length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {}".format(ldf.table_name), ldf.SQLconnection) + ldf.length = list(length_query['length'])[0] self.get_unique_values(ldf) #ldf.get_cardinality() From 16ded3a6480d7a17c6a4fb7c49f7064c4ecc4497 Mon Sep 17 00:00:00 2001 From: cjachekang <47467363+cjachekang@users.noreply.github.com> Date: Fri, 23 Oct 2020 03:13:35 -0700 Subject: [PATCH 018/114] Adding support for setting intent on front end (#112) * added functionality to delete Vis * fixed deletion logic * add observer to automatically update deletions * able to refresh widget on setting intent * support for setting intent from frontend * quick fix to output * changed variable intentindex name --- lux/core/frame.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index ab497a54..6ef8b775 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -530,13 +530,32 @@ def exported(self) -> Union[Dict[str,VisList], VisList]: ,stacklevel=2) return [] - def removeDeletedRecs(self, change): + def remove_deleted_recs(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 set_intent_on_click(self, change): + from IPython.display import display, clear_output + from lux.processor.Compiler import Compiler + + intent_action = list(self._widget.selectedIntentIndex.keys())[0] + vis = self.recommendation[intent_action][self._widget.selectedIntentIndex[intent_action][0]] + self.set_intent_as_vis(vis) + + self.maintain_metadata() + self.current_vis = Compiler.compile_intent(self, self._intent) + self.maintain_recs() + + with self.output: + clear_output() + display(self._widget) + + self._widget.observe(self.remove_deleted_recs, names='deletedIndices') + self._widget.observe(self.set_intent_on_click, names='selectedIntentIndex') + def _repr_html_(self): from IPython.display import display from IPython.display import clear_output @@ -578,18 +597,19 @@ def _repr_html_(self): self.maintain_recs() #Observers(callback_function, listen_to_this_variable) - self._widget.observe(self.removeDeletedRecs, names='deletedIndices') + self._widget.observe(self.remove_deleted_recs, names='deletedIndices') + self._widget.observe(self.set_intent_on_click, names='selectedIntentIndex') 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() + self.output = widgets.Output() # box.children = [button,output] # output.children = [button] # display(box) - display(button,output) + display(button, self.output) def on_button_clicked(b): - with output: + with self.output: if (b): self._toggle_pandas_display = not self._toggle_pandas_display clear_output() @@ -604,6 +624,7 @@ def on_button_clicked(b): 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: From b825c51d16a47dd4de83bf268b83c53d70454fdc Mon Sep 17 00:00:00 2001 From: Caitlyn Chen Date: Sat, 24 Oct 2020 23:38:02 -0700 Subject: [PATCH 019/114] Make default_display a global setting (#121) * remove and register action functions * update changes inframe.py * update changes inframe.py * add documentation and changes * indentation and comments * new line * globally defined default display works with warning * no examples * add back space * new line * uncomment docstring Co-authored-by: Caitlyn Chen --- lux/__init__.py | 3 ++- lux/_config/__init__.py | 1 + lux/_config/config.py | 29 ++++++++++++++++++++++++++++- lux/core/frame.py | 26 ++++---------------------- 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/lux/__init__.py b/lux/__init__.py index 2d149806..8688b3d2 100644 --- a/lux/__init__.py +++ b/lux/__init__.py @@ -21,5 +21,6 @@ register_action, remove_action, actions, - update_actions + update_actions, + config, ) diff --git a/lux/_config/__init__.py b/lux/_config/__init__.py index 2d70a068..f7130949 100644 --- a/lux/_config/__init__.py +++ b/lux/_config/__init__.py @@ -4,4 +4,5 @@ remove_action, actions, update_actions, + config, ) diff --git a/lux/_config/config.py b/lux/_config/config.py index a5d6992f..12a7271b 100644 --- a/lux/_config/config.py +++ b/lux/_config/config.py @@ -4,6 +4,7 @@ ''' from collections import namedtuple from typing import Any, Callable, Dict, Iterable, List, Optional +import warnings RegisteredOption = namedtuple("RegisteredOption", "name action display_condition args") @@ -142,4 +143,30 @@ def is_callable(obj) -> bool: if not callable(obj): raise ValueError("Value must be a callable") return True - \ No newline at end of file + +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() diff --git a/lux/core/frame.py b/lux/core/frame.py index 6ef8b775..27227bc3 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 (self._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) - """ - if (type.lower()=="lux"): - self._default_pandas_display = False - elif (type.lower()=="pandas"): - self._default_pandas_display = True - 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 @@ -591,7 +570,10 @@ def _repr_html_(self): from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) - self._toggle_pandas_display = self._default_pandas_display # Reset to Pandas Vis everytime + if (lux.config.default_display == "lux"): + self._toggle_pandas_display = False + else: + self._toggle_pandas_display = True # 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() From d65687aec259a4956799f6f840db512a9a08ba07 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sun, 25 Oct 2020 10:00:39 -0700 Subject: [PATCH 020/114] Added script to generate Postgresql database Updated travis.yml file to create postgresql database in test instance. Added script to populate test database with data. --- .travis.yml | 9 +++++++++ lux/data/upload_car_data.py | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 lux/data/upload_car_data.py diff --git a/.travis.yml b/.travis.yml index c087fe4d..721d96a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,22 @@ language: python python: - "3.7" +services: + - postgresql install: - pip install codecov - pip install -r requirements.txt - pip install git+https://github.com/lux-org/lux-widget #- npm i lux-widget +before_script: + - psql -c "ALTER USER postgres WITH PASSWORD 'lux';" -U postgres + - psql -c "ALTER USER postgres WITH SUPERUSER;" -U postgres + - psql -c "ALTER DATABASE postgres OWNER TO travis;" + - psql -c "DROP schema public cascade;" -U postgres + - psql -c "CREATE schema public;" -U postgres # command to run tests script: + - python lux/data/upload_car.py - python -m pytest tests/*.py - pytest --cov-report term --cov=lux tests/ after_success: diff --git a/lux/data/upload_car_data.py b/lux/data/upload_car_data.py new file mode 100644 index 00000000..8950dc48 --- /dev/null +++ b/lux/data/upload_car_data.py @@ -0,0 +1,39 @@ +import pandas as pd +from sqlalchemy import create_engine +import psycopg2 +import csv + +import psycopg2 +conn = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=dbadmin") +cur = conn.cursor() +cur.execute(""" + DROP TABLE IF EXISTS car + """) +#create car table in postgres database +cur.execute(""" + CREATE TABLE car( + name text, + milespergal numeric, + cylinders integer, + displacement numeric, + horsepower integer, + weight integer, + acceleration numeric, + year integer, + origin text +) +""") + +#open car.csv and read data into database +with open('car.csv', 'r') as f: + reader = csv.reader(f) + next(reader) # Skip the header row. + i = 0 + for row in reader: + #print(row) + cur.execute( + "INSERT INTO car VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)", + row + ) + +conn.commit() \ No newline at end of file From 7243b2f5e19d5be6db172edd6f6d404c1294b7e2 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sun, 25 Oct 2020 10:01:46 -0700 Subject: [PATCH 021/114] Update upload_car_data.py Updated database credentials --- lux/data/upload_car_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lux/data/upload_car_data.py b/lux/data/upload_car_data.py index 8950dc48..b565861b 100644 --- a/lux/data/upload_car_data.py +++ b/lux/data/upload_car_data.py @@ -4,7 +4,7 @@ import csv import psycopg2 -conn = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=dbadmin") +conn = psycopg2.connect("host=localhost dbname=public user=postgres password=lux") cur = conn.cursor() cur.execute(""" DROP TABLE IF EXISTS car From 2add76f9a54198eebbf483977ebb17d2509d87bf Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sun, 25 Oct 2020 10:10:07 -0700 Subject: [PATCH 022/114] Updated script name in travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 721d96a6..eed464b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ before_script: - psql -c "CREATE schema public;" -U postgres # command to run tests script: - - python lux/data/upload_car.py + - python lux/data/upload_car_data.py - python -m pytest tests/*.py - pytest --cov-report term --cov=lux tests/ after_success: From cf74bebdd8c211446774a4566785f55481ee8ab5 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sun, 25 Oct 2020 10:17:42 -0700 Subject: [PATCH 023/114] Removed unnecessary import from travis.yml --- lux/data/upload_car_data.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lux/data/upload_car_data.py b/lux/data/upload_car_data.py index b565861b..a52c1823 100644 --- a/lux/data/upload_car_data.py +++ b/lux/data/upload_car_data.py @@ -1,5 +1,4 @@ import pandas as pd -from sqlalchemy import create_engine import psycopg2 import csv From 14d52b8a85fe2f6e2c47d5f1e1ec6b1180af3d9a Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sun, 25 Oct 2020 10:29:05 -0700 Subject: [PATCH 024/114] Added psycopg2 to requirements.txt --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 722203d1..526d7107 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,5 +10,5 @@ Sphinx>=3.0.2 sphinx-rtd-theme>=0.4.3 lux-widget==0.1.0 # Install only to use SQLExecutor -# psycopg2>=2.8.5 -# psycopg2-binary>=2.8.5 +psycopg2>=2.8.5 +psycopg2-binary>=2.8.5 From 379517d253b6be90bcf08172d23cf4e1d8771bfe Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sun, 25 Oct 2020 10:44:40 -0700 Subject: [PATCH 025/114] Creating Postgres test database in travis --- .travis.yml | 3 ++- lux/data/upload_car_data.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index eed464b7..02918644 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,8 @@ before_script: - psql -c "ALTER USER postgres WITH SUPERUSER;" -U postgres - psql -c "ALTER DATABASE postgres OWNER TO travis;" - psql -c "DROP schema public cascade;" -U postgres - - psql -c "CREATE schema public;" -U postgres + - psql -c "CREATE schema public;" -U postgres + - psql -c "CREATE DATABASE postgres_db;" -U postgres # command to run tests script: - python lux/data/upload_car_data.py diff --git a/lux/data/upload_car_data.py b/lux/data/upload_car_data.py index a52c1823..52dcde35 100644 --- a/lux/data/upload_car_data.py +++ b/lux/data/upload_car_data.py @@ -3,7 +3,7 @@ import csv import psycopg2 -conn = psycopg2.connect("host=localhost dbname=public user=postgres password=lux") +conn = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") cur = conn.cursor() cur.execute(""" DROP TABLE IF EXISTS car From a72f236960dc7be2ec415db5bf088055a9b08953 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sun, 25 Oct 2020 10:59:28 -0700 Subject: [PATCH 026/114] Fixed directory issue --- lux/data/upload_car_data.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lux/data/upload_car_data.py b/lux/data/upload_car_data.py index 52dcde35..289f7003 100644 --- a/lux/data/upload_car_data.py +++ b/lux/data/upload_car_data.py @@ -24,12 +24,11 @@ """) #open car.csv and read data into database -with open('car.csv', 'r') as f: +with open('lux/data/car.csv', 'r') as f: reader = csv.reader(f) next(reader) # Skip the header row. i = 0 for row in reader: - #print(row) cur.execute( "INSERT INTO car VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)", row From e947fd64bf77adaa4b63508cbcd2fca794bf3ffa Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Mon, 26 Oct 2020 13:55:54 -0700 Subject: [PATCH 027/114] Updated SQL Executor Tests Added tests for basic SQL Executor functionality. --- tests/test_sql_executor.py | 316 ++++++++++++++++++------------------- 1 file changed, 158 insertions(+), 158 deletions(-) diff --git a/tests/test_sql_executor.py b/tests/test_sql_executor.py index 9932e815..1cf31775 100644 --- a/tests/test_sql_executor.py +++ b/tests/test_sql_executor.py @@ -1,171 +1,171 @@ -# # 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 -# from lux.executor.SQLExecutor import SQLExecutor -# from lux.vis.Vis import Vis -# from lux.vis.VisList import VisList -# import psycopg2 - -# def test_lazy_execution(): -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") - -# intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] -# vis = Vis(intent) -# # Check data field in vis is empty before calling executor -# assert vis.data is None -# SQLExecutor.execute([vis], sql_df) -# assert type(vis.data) == lux.core.frame.LuxDataFrame +# 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 +from lux.executor.SQLExecutor import SQLExecutor +from lux.vis.Vis import Vis +from lux.vis.VisList import VisList +import psycopg2 + +def test_lazy_execution(): + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + + intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] + vis = Vis(intent) + # Check data field in vis is empty before calling executor + assert vis.data is None + SQLExecutor.execute([vis], sql_df) + assert type(vis.data) == lux.core.frame.LuxDataFrame -# def test_selection(): -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") - -# intent = [lux.Clause(attribute = ["horsepower", "weight", "acceleration"]), lux.Clause(attribute ="year")] -# vislist = VisList(intent,sql_df) -# assert all([type(vis.data) == lux.core.frame.LuxDataFrame for vis in vislist]) -# assert all(vislist[2].data.columns == ["year", 'acceleration']) - -# def test_aggregation(): -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") - -# intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] -# vis = Vis(intent,sql_df) -# result_df = vis.data -# assert int(result_df[result_df["origin"]=="USA"]["horsepower"])==119 - -# intent = [lux.Clause(attribute ="horsepower", aggregation="sum"), lux.Clause(attribute ="origin")] -# vis = Vis(intent,sql_df) -# result_df = vis.data -# assert int(result_df[result_df["origin"]=="Japan"]["horsepower"])==6307 - -# intent = [lux.Clause(attribute ="horsepower", aggregation="max"), lux.Clause(attribute ="origin")] -# vis = Vis(intent,sql_df) -# result_df = vis.data -# assert int(result_df[result_df["origin"]=="Europe"]["horsepower"])==133 - -# def test_colored_bar_chart(): -# from lux.vis.Vis import Vis -# from lux.vis.Vis import Clause - -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") - -# x_clause = Clause(attribute = "milespergal", channel = "x") -# y_clause = Clause(attribute = "origin", channel = "y") -# color_clause = Clause(attribute = 'cylinders', channel = "color") - -# new_vis = Vis([x_clause, y_clause, color_clause],sql_df) -# #make sure dimention of the data is correct -# color_cardinality = len(sql_df.unique_values['cylinders']) -# group_by_cardinality = len(sql_df.unique_values['origin']) -# assert (len(new_vis.data.columns)==3) -# assert(len(new_vis.data)==15 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values +def test_selection(): + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + + intent = [lux.Clause(attribute = ["horsepower", "weight", "acceleration"]), lux.Clause(attribute ="year")] + vislist = VisList(intent,sql_df) + assert all([type(vis.data) == lux.core.frame.LuxDataFrame for vis in vislist]) + assert all(vislist[2].data.columns == ["year", 'acceleration']) + +def test_aggregation(): + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + + intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] + vis = Vis(intent,sql_df) + result_df = vis.data + assert int(result_df[result_df["origin"]=="USA"]["horsepower"])==119 + + intent = [lux.Clause(attribute ="horsepower", aggregation="sum"), lux.Clause(attribute ="origin")] + vis = Vis(intent,sql_df) + result_df = vis.data + assert int(result_df[result_df["origin"]=="Japan"]["horsepower"])==6307 + + intent = [lux.Clause(attribute ="horsepower", aggregation="max"), lux.Clause(attribute ="origin")] + vis = Vis(intent,sql_df) + result_df = vis.data + assert int(result_df[result_df["origin"]=="Europe"]["horsepower"])==133 + +def test_colored_bar_chart(): + from lux.vis.Vis import Vis + from lux.vis.Vis import Clause + + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + + x_clause = Clause(attribute = "milespergal", channel = "x") + y_clause = Clause(attribute = "origin", channel = "y") + color_clause = Clause(attribute = 'cylinders', channel = "color") + + new_vis = Vis([x_clause, y_clause, color_clause],sql_df) + #make sure dimention of the data is correct + color_cardinality = len(sql_df.unique_values['cylinders']) + group_by_cardinality = len(sql_df.unique_values['origin']) + assert (len(new_vis.data.columns)==3) + assert(len(new_vis.data)==15 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values -# def test_colored_line_chart(): -# from lux.vis.Vis import Vis -# from lux.vis.Vis import Clause +def test_colored_line_chart(): + from lux.vis.Vis import Vis + from lux.vis.Vis import Clause -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") -# x_clause = Clause(attribute = "year", channel = "x") -# y_clause = Clause(attribute = "milespergal", channel = "y") -# color_clause = Clause(attribute = 'cylinders', channel = "color") + x_clause = Clause(attribute = "year", channel = "x") + y_clause = Clause(attribute = "milespergal", channel = "y") + color_clause = Clause(attribute = 'cylinders', channel = "color") -# new_vis = Vis([x_clause, y_clause, color_clause],sql_df) + new_vis = Vis([x_clause, y_clause, color_clause],sql_df) -# #make sure dimention of the data is correct -# color_cardinality = len(sql_df.unique_values['cylinders']) -# group_by_cardinality = len(sql_df.unique_values['year']) -# assert (len(new_vis.data.columns)==3) -# assert(len(new_vis.data)==60 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values + #make sure dimention of the data is correct + color_cardinality = len(sql_df.unique_values['cylinders']) + group_by_cardinality = len(sql_df.unique_values['year']) + assert (len(new_vis.data.columns)==3) + assert(len(new_vis.data)==60 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values -# def test_filter(): -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") - -# intent = [lux.Clause(attribute ="horsepower"), lux.Clause(attribute ="year"), lux.Clause(attribute ="origin", filter_op="=", value ="USA")] -# vis = Vis(intent,sql_df) -# vis._vis_data = sql_df -# filter_output = SQLExecutor.execute_filter(vis) -# assert filter_output[0] == "WHERE origin = 'USA'" -# assert filter_output[1] == ['origin'] - -# def test_inequalityfilter(): -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") -# vis = Vis([lux.Clause(attribute ="horsepower", filter_op=">", value=50), lux.Clause(attribute ="milespergal")]) -# vis._vis_data = sql_df -# filter_output = SQLExecutor.execute_filter(vis) -# assert filter_output[0] == "WHERE horsepower > '50'" -# assert filter_output[1] == ['horsepower'] +def test_filter(): + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + + intent = [lux.Clause(attribute ="horsepower"), lux.Clause(attribute ="year"), lux.Clause(attribute ="origin", filter_op="=", value ="USA")] + vis = Vis(intent,sql_df) + vis._vis_data = sql_df + filter_output = SQLExecutor.execute_filter(vis) + assert filter_output[0] == "WHERE origin = 'USA'" + assert filter_output[1] == ['origin'] + +def test_inequalityfilter(): + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute ="horsepower", filter_op=">", value=50), lux.Clause(attribute ="milespergal")]) + vis._vis_data = sql_df + filter_output = SQLExecutor.execute_filter(vis) + assert filter_output[0] == "WHERE horsepower > '50'" + assert filter_output[1] == ['horsepower'] -# intent = [lux.Clause(attribute ="horsepower", filter_op="<=", value=100), lux.Clause(attribute ="milespergal")] -# vis = Vis(intent,sql_df) -# vis._vis_data = sql_df -# filter_output = SQLExecutor.execute_filter(vis) -# assert filter_output[0] == "WHERE horsepower <= '100'" -# assert filter_output[1] == ['horsepower'] + intent = [lux.Clause(attribute ="horsepower", filter_op="<=", value=100), lux.Clause(attribute ="milespergal")] + vis = Vis(intent,sql_df) + vis._vis_data = sql_df + filter_output = SQLExecutor.execute_filter(vis) + assert filter_output[0] == "WHERE horsepower <= '100'" + assert filter_output[1] == ['horsepower'] -# def test_binning(): -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") -# vis = Vis([lux.Clause(attribute ="horsepower")],sql_df) -# nbins =list(filter(lambda x: x.bin_size!=0, vis._inferred_intent))[0].bin_size -# assert len(vis.data) == nbins - -# def test_record(): -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") -# vis = Vis([lux.Clause(attribute ="cylinders")],sql_df) -# assert len(vis.data) == len(sql_df.unique_values["cylinders"]) +def test_binning(): + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute ="horsepower")],sql_df) + nbins =list(filter(lambda x: x.bin_size!=0, vis._inferred_intent))[0].bin_size + assert len(vis.data) == nbins + +def test_record(): + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute ="cylinders")],sql_df) + assert len(vis.data) == len(sql_df.unique_values["cylinders"]) -# def test_filter_aggregation_fillzero_aligned(): -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") -# intent = [lux.Clause(attribute="cylinders"), lux.Clause(attribute="milespergal"), lux.Clause("origin=Japan")] -# vis = Vis(intent,sql_df) -# result = vis.data -# assert result[result["cylinders"]==5]["milespergal"].values[0]==0 -# assert result[result["cylinders"]==8]["milespergal"].values[0]==0 - -# def test_exclude_attribute(): -# connection = psycopg2.connect("dbname=adventureworks user=postgres password=dbadmin") -# sql_df = lux.LuxDataFrame() -# sql_df.set_SQL_connection(connection, "cars") -# intent = [lux.Clause("?", exclude=["name", "year"]), lux.Clause("horsepower")] -# vislist = VisList(intent,sql_df) -# for vis in vislist: -# assert (vis.get_attr_by_channel("x")[0].attribute != "year") -# assert (vis.get_attr_by_channel("x")[0].attribute != "name") -# assert (vis.get_attr_by_channel("y")[0].attribute != "year") -# assert (vis.get_attr_by_channel("y")[0].attribute != "year") +def test_filter_aggregation_fillzero_aligned(): + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + intent = [lux.Clause(attribute="cylinders"), lux.Clause(attribute="milespergal"), lux.Clause("origin=Japan")] + vis = Vis(intent,sql_df) + result = vis.data + assert result[result["cylinders"]==5]["milespergal"].values[0]==0 + assert result[result["cylinders"]==8]["milespergal"].values[0]==0 + +def test_exclude_attribute(): + connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + intent = [lux.Clause("?", exclude=["name", "year"]), lux.Clause("horsepower")] + vislist = VisList(intent,sql_df) + for vis in vislist: + assert (vis.get_attr_by_channel("x")[0].attribute != "year") + assert (vis.get_attr_by_channel("x")[0].attribute != "name") + assert (vis.get_attr_by_channel("y")[0].attribute != "year") + assert (vis.get_attr_by_channel("y")[0].attribute != "year") From 0fb9421176ce3d823dc56fdcec8ab29ca30781f8 Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Thu, 29 Oct 2020 11:24:26 +0800 Subject: [PATCH 028/114] Update requirements.txt (#128) * basic scatterplot experiments * experiment results with manually binned heatmaps * experiment result * incorporated heatmap code into executor and renderer * additional experiments to evaluate scatter v.s. heatmap performance * experiment based on real estate and airbnb data * modified general sampling criteria, suppress SettingWithCopyWarning stemming from groupby .agg (#93) * decrease sampling parameter * change sampling strategy (above threshold keep 3/4 of data) * remove experiment dir * modified performance param * enforce lux-widget minimum version * update requirement.txt * separate dev and install requirements --- .travis.yml | 3 +-- requirements-dev.txt | 4 ++++ requirements.txt | 11 +---------- 3 files changed, 6 insertions(+), 12 deletions(-) create mode 100644 requirements-dev.txt diff --git a/.travis.yml b/.travis.yml index c087fe4d..fcd12c05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,9 @@ language: python python: - "3.7" install: - - pip install codecov - pip install -r requirements.txt + - pip install -r requirements-dev.txt - pip install git+https://github.com/lux-org/lux-widget - #- npm i lux-widget # command to run tests script: - python -m pytest tests/*.py diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 00000000..3d919655 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,4 @@ +pytest>=5.3.1 +pytest-cov>=2.8.1 +Sphinx>=3.0.2 +sphinx-rtd-theme>=0.4.3 diff --git a/requirements.txt b/requirements.txt index 722203d1..0b31c7a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,5 @@ scipy>=1.3.3 altair>=4.0.0 -jupyter -vega_datasets pandas>=1.1.0 -pytest>=5.3.1 -pytest-cov>=2.8.1 scikit-learn>=0.22 -Sphinx>=3.0.2 -sphinx-rtd-theme>=0.4.3 -lux-widget==0.1.0 -# Install only to use SQLExecutor -# psycopg2>=2.8.5 -# psycopg2-binary>=2.8.5 +lux-widget>=0.1.0 \ No newline at end of file From a06d96820a5d5ab0c6093b17f7bb64429a1b7964 Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Thu, 29 Oct 2020 13:49:17 +0800 Subject: [PATCH 029/114] replaced _exportedVisIdxs --> _selectedVisIdxs --- lux/core/frame.py | 6 +++--- lux/vis/VisList.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 27227bc3..285e47f7 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -458,8 +458,8 @@ def exported(self) -> Union[Dict[str,VisList], VisList]: Notes ----- - Convert the _exportedVisIdxs dictionary into a programmable VisList - Example _exportedVisIdxs : + Convert the _selectedVisIdxs dictionary into a programmable VisList + Example _selectedVisIdxs : {'Correlation': [0, 2], 'Occurrence': [1]} indicating the 0th and 2nd vis from the `Correlation` tab is selected, and the 1st vis from the `Occurrence` tab is selected. @@ -477,7 +477,7 @@ 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._selectedVisIdxs exported_vis = [] if (exported_vis_lst=={}): if self._saved_export: diff --git a/lux/vis/VisList.py b/lux/vis/VisList.py index 0cfdd6b7..1aec2f62 100644 --- a/lux/vis/VisList.py +++ b/lux/vis/VisList.py @@ -62,8 +62,8 @@ def exported(self) -> VisList: Notes ----- - Convert the _exportedVisIdxs dictionary into a programmable VisList - Example _exportedVisIdxs : + Convert the _selectedVisIdxs dictionary into a programmable VisList + Example _selectedVisIdxs : {'Vis List': [0, 2]} Returns @@ -78,7 +78,7 @@ def exported(self) -> 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._selectedVisIdxs if (exported_vis_lst=={}): warnings.warn( "\nNo visualization selected to export.\n" From f740e89964e0e2c069a07fb67c301bac7a5159c2 Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Fri, 30 Oct 2020 17:45:52 +0800 Subject: [PATCH 030/114] bugfix: plot config error when current_vis is None --- lux/core/frame.py | 3 ++- lux/vislib/altair/AltairRenderer.py | 2 +- tests/test_config.py | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lux/core/frame.py b/lux/core/frame.py index 285e47f7..112afb53 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -433,7 +433,8 @@ def maintain_recs(self): action_type = rec_info["action"] vlist = rec_info["collection"] if (rec_df._plot_config): - for vis in rec_df.current_vis: vis._plot_config = rec_df.plot_config + if (rec_df.current_vis): + for vis in rec_df.current_vis: vis._plot_config = rec_df.plot_config for vis in vlist: vis._plot_config = rec_df.plot_config if (len(vlist)>0): rec_df.recommendation[action_type] = vlist diff --git a/lux/vislib/altair/AltairRenderer.py b/lux/vislib/altair/AltairRenderer.py index 3f886042..0e9ebe54 100644 --- a/lux/vislib/altair/AltairRenderer.py +++ b/lux/vislib/altair/AltairRenderer.py @@ -71,8 +71,8 @@ def create_vis(self,vis, standalone=True): chart = None if (chart): + if (vis.plot_config): chart.chart = vis.plot_config(chart.chart) if (self.output_type=="VegaLite"): - if (vis.plot_config): chart.chart = vis.plot_config(chart.chart) chart_dict = chart.chart.to_dict() # this is a bit of a work around because altair must take a pandas dataframe and we can only generate a luxDataFrame # chart["data"] = { "values": vis.data.to_dict(orient='records') } diff --git a/tests/test_config.py b/tests/test_config.py index 8aee0748..3b9be963 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -138,3 +138,20 @@ def test_remove_default_actions(): assert("bars" in df.recommendation, "Bars should be rendered after it has been registered with correct intent.") assert(len(df.recommendation["bars"]) > 0) + +# TODO: This test does not pass in pytest but is working in Jupyter notebook. +# def test_plot_setting(): +# df = pd.read_csv("lux/data/car.csv") +# df["Year"] = pd.to_datetime(df["Year"], format='%Y') +# def change_color_add_title(chart): +# chart = chart.configure_mark(color="green") # change mark color to green +# chart.title = "Custom Title" # add title to chart +# return chart + +# df.plot_config = change_color_add_title + +# df._repr_html_() + +# vis_code = df.recommendation["Correlation"][0].to_Altair() +# print (vis_code) +# assert 'chart = chart.configure_mark(color="green")' in vis_code, "Exported chart does not have additional plot style setting." \ No newline at end of file From 1234009059e0fdc235c6deeffc3d1d8b0583c29c Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sat, 31 Oct 2020 09:38:43 -0700 Subject: [PATCH 031/114] Added sql_executor example notebook, minor bug fix Added an example notebook to showcase how to use the sql-engine. Fixed variable reference in interestingness.py that was causing issues. --- examples/Postgres_Executor_Example.ipynb | 222 +++++++++++++++++++++++ lux/core/frame.py | 2 +- lux/data/upload_flights_data.py | 45 +++++ lux/interestingness/interestingness.py | 4 +- tests/test_compiler.py | 1 + 5 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 examples/Postgres_Executor_Example.ipynb create mode 100644 lux/data/upload_flights_data.py diff --git a/examples/Postgres_Executor_Example.ipynb b/examples/Postgres_Executor_Example.ipynb new file mode 100644 index 00000000..7a3bc52a --- /dev/null +++ b/examples/Postgres_Executor_Example.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Users\\thyne\\Documents\\GitHub\\thyne-lux\n" + ] + } + ], + "source": [ + "cd ../" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can upload the car dataset to a local postgres database using the upload_car_data.py script in the lux/data folder. You will need to update the name of the database and the login credentials in that file." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "73ec3be76cda474e8becf0c90299fd89", + "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": "05bb45135cd94c3995bdd7b55a186d9f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import lux\n", + "import psycopg2\n", + "import pandas as pd\n", + "\n", + "connection = psycopg2.connect(\"host=localhost dbname=postgres_db user=postgres password=lux\")\n", + "\n", + "sql_df = lux.LuxDataFrame()\n", + "sql_df.set_SQL_connection(connection, \"car\")\n", + "sql_df" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'quantitative': ['milespergal',\n", + " 'displacement',\n", + " 'horsepower',\n", + " 'weight',\n", + " 'acceleration'],\n", + " 'ordinal': [],\n", + " 'nominal': ['name', 'cylinders', 'origin'],\n", + " 'temporal': ['year'],\n", + " 'id': []}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sql_df.data_type" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9ee7f6a218fb4724b7dd289337f480e6", + "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": "fced58dbb4cf48c28d8598a0e944ee0e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sql_df.set_intent([\"milespergal\"])\n", + "sql_df" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3411914c92ac42a2a7a6bb7b3ee472f6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "LuxWidget(current_vis={'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}, 'axis': {'labelCo…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from lux.vis.Vis import Vis\n", + "from lux.vis.Vis import Clause\n", + "\n", + "x_clause = Clause(attribute = \"milespergal\", channel = \"x\")\n", + "y_clause = Clause(attribute = \"weight\", channel = \"y\")\n", + "color_clause = Clause(attribute = 'cylinders', channel = \"color\")\n", + "filter_clause = Clause(attribute =\"horsepower\", filter_op=\">\", value=150)\n", + "\n", + "new_vis = Vis([x_clause, y_clause, color_clause])\n", + "new_vis.refresh_source(sql_df)\n", + "new_vis" + ] + } + ], + "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 86b46c86..14ce35c5 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -87,7 +87,7 @@ def maintain_metadata(self): def expire_recs(self): self._recs_fresh = False self.recommendation = {} - self.current_vis = None + self.current_vis = [] self._widget = None self._rec_info = None self._sampled = None diff --git a/lux/data/upload_flights_data.py b/lux/data/upload_flights_data.py new file mode 100644 index 00000000..0ea65c63 --- /dev/null +++ b/lux/data/upload_flights_data.py @@ -0,0 +1,45 @@ +import pandas as pd +from sqlalchemy import create_engine +import psycopg2 +import csv + +import psycopg2 +conn = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") +cur = conn.cursor() +cur.execute(""" + DROP TABLE IF EXISTS flights + """) + +#create flights table in postgres database +cur.execute(""" + CREATE TABLE flights( + year integer, + month text, + day integer, + weekday integer, + carrier text, + origin text, + destination text, + arrivaldelay integer, + depaturedelay integer, + weatherdelay integer, + distance integer +) +""") + +#open flights.csv and read data into database +with open('flights.csv', 'r') as f: + reader = csv.reader(f) + next(reader) # Skip the header row. + i = 0 + for row in reader: + if i%50000 == 0: + print(i) + i+=1 + #print(row) + cur.execute( + "INSERT INTO flights VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", + row + ) + +conn.commit() \ No newline at end of file diff --git a/lux/interestingness/interestingness.py b/lux/interestingness/interestingness.py index c38df266..b52ec770 100644 --- a/lux/interestingness/interestingness.py +++ b/lux/interestingness/interestingness.py @@ -180,8 +180,8 @@ def deviation_from_overall(vis:Vis, ldf:LuxDataFrame, filter_specs:list, msr_att v_filter_size = get_filtered_size(filter_specs, ldf) v_size = len(vis.data) else: - v_filter_size = vis._vis_data.num_obs - v_size = ldf.num_obs + v_filter_size = vis._vis_data.length + v_size = ldf.length v_filter = vis.data[msr_attribute] total = v_filter.sum() v_filter = v_filter/total # normalize by total to get ratio diff --git a/tests/test_compiler.py b/tests/test_compiler.py index cda948d1..875e2ea6 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -17,6 +17,7 @@ import pandas as pd from lux.vis.Vis import Vis from lux.vis.VisList import VisList +import psycopg2 def test_underspecified_no_vis(test_recs): no_vis_actions = ["Correlation", "Distribution", "Occurrence","Temporal"] From 6a20c47d3f44c0e9d4151bd379e4953ceb7bf68f Mon Sep 17 00:00:00 2001 From: Kunal Agarwal <32151899+westernguy2@users.noreply.github.com> Date: Sun, 1 Nov 2020 21:56:56 -0800 Subject: [PATCH 032/114] Add LuxSeries Implementation (#122) * add preliminary groupby fixes * preliminary LuxSeries implementation * add tests for new Series implementation * clean up the added code * minor code changes * fix issues with Vis with index * small fixes * remove comments * bugfix column group display empty Vis involving groupby index * bugfix Cylinders not showing up as bar charts Co-authored-by: Doris Lee --- lux/action/column_group.py | 14 ++++-- lux/core/frame.py | 22 +++++---- lux/core/series.py | 19 ++++---- lux/executor/PandasExecutor.py | 2 +- lux/processor/Compiler.py | 20 ++++---- lux/utils/utils.py | 2 +- tests/test_maintainence.py | 6 +++ tests/test_pandas_coverage.py | 83 ++++++++++++++++------------------ 8 files changed, 90 insertions(+), 78 deletions(-) diff --git a/lux/action/column_group.py b/lux/action/column_group.py index 054acda9..ba1b9e5a 100644 --- a/lux/action/column_group.py +++ b/lux/action/column_group.py @@ -30,13 +30,17 @@ def column_group(ldf): ldf_flat.columns = ldf_flat.columns.format() ldf_flat = ldf_flat.reset_index() #use a single shared ldf_flat so that metadata doesn't need to be computed for every vis if (ldf.index.nlevels==1): - index_column_name = ldf.index.name + if ldf.index.name: + index_column_name = ldf.index.name + else: + index_column_name = "index" if isinstance(ldf.columns,pd.DatetimeIndex): ldf.columns = ldf.columns.to_native_types() - for attribute in ldf.columns: - vis = Vis([index_column_name,lux.Clause(str(attribute),aggregation=None)],ldf_flat) - collection.append(vis) - vlst = VisList(collection) + for attribute in ldf.columns: + if ldf[attribute].dtype!="object" and (attribute!="index"): + vis = Vis([lux.Clause(index_column_name, data_type = "nominal", data_model = "dimension", aggregation=None), lux.Clause(str(attribute), data_type = "quantitative", aggregation=None)]) + collection.append(vis) + vlst = VisList(collection,ldf_flat) # Note that we are not computing interestingness score here because we want to preserve the arrangement of the aggregated ldf recommendation["collection"] = vlst diff --git a/lux/core/frame.py b/lux/core/frame.py index 112afb53..bd7cea6a 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -66,12 +66,15 @@ def __init__(self,*args, **kw): @property def _constructor(self): return LuxDataFrame - # @property - # def _constructor_sliced(self): - # def f(*args, **kwargs): - # # adapted from https://github.com/pandas-dev/pandas/issues/13208#issuecomment-326556232 - # return LuxSeries(*args, **kwargs).__finalize__(self, method='inherit') - # return f + @property + def _constructor_sliced(self): + def f(*args, **kwargs): + s = LuxSeries(*args, **kwargs) + for attr in self._metadata: #propagate metadata + s.__dict__[attr] = getattr(self, attr, None) + return s + return f + @property def history(self): return self._history @@ -385,7 +388,7 @@ def maintain_recs(self): 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._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 @@ -400,10 +403,9 @@ def maintain_recs(self): if (rec_df.pre_aggregated): if (rec_df.columns.name is not None): rec_df._append_rec(rec_infolist, row_group(rec_df)) - if (rec_df.index.name is not None): - rec_df._append_rec(rec_infolist, column_group(rec_df)) + rec_df._append_rec(rec_infolist, column_group(rec_df)) else: - if self.recommendation == {}: + if rec_df.recommendation == {}: # 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 diff --git a/lux/core/series.py b/lux/core/series.py index e0179362..ecfd9aa2 100644 --- a/lux/core/series.py +++ b/lux/core/series.py @@ -14,11 +14,12 @@ import pandas as pd class LuxSeries(pd.Series): - # _metadata = ['name','_intent','data_type_lookup','data_type', - # 'data_model_lookup','data_model','unique_values','cardinality', - # 'min_max','plot_config', '_current_vis','_widget', '_recommendation'] + _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'] def __init__(self,*args, **kw): super(LuxSeries, self).__init__(*args, **kw) + @property def _constructor(self): return LuxSeries @@ -26,8 +27,10 @@ def _constructor(self): @property def _constructor_expanddim(self): from lux.core.frame import LuxDataFrame - # def f(*args, **kwargs): - # # adapted from https://github.com/pandas-dev/pandas/issues/13208#issuecomment-326556232 - # return LuxDataFrame(*args, **kwargs).__finalize__(self, method='inherit') - # return f - return LuxDataFrame \ No newline at end of file + def f(*args, **kwargs): + df = LuxDataFrame(*args, **kwargs) + for attr in self._metadata: + df.__dict__[attr] = getattr(self, attr, None) + return df + f._get_axis_number = super(LuxSeries, self)._get_axis_number + return f \ No newline at end of file diff --git a/lux/executor/PandasExecutor.py b/lux/executor/PandasExecutor.py index 6924886d..a8d17d36 100644 --- a/lux/executor/PandasExecutor.py +++ b/lux/executor/PandasExecutor.py @@ -293,7 +293,7 @@ def execute_2D_binning(vis: Vis): result = result.dropna() else: groups = vis._vis_data.groupby(['xBin','yBin'])[x_attr.attribute] - result = groups.agg("count").reset_index() # .agg in this line throws SettingWithCopyWarning + result = groups.agg("count").reset_index(name=x_attr.attribute) # .agg in this line throws SettingWithCopyWarning result = result.rename(columns={x_attr.attribute:"count"}) result = result[result["count"]!=0] diff --git a/lux/processor/Compiler.py b/lux/processor/Compiler.py index 8b513c24..17edb97a 100644 --- a/lux/processor/Compiler.py +++ b/lux/processor/Compiler.py @@ -144,12 +144,12 @@ def populate_data_type_model(ldf, vis_collection) -> VisList: clause.description = "" # TODO: Note that "and not is_datetime_string(clause.attribute))" is a temporary hack and breaks the `test_row_column_group` example if (clause.attribute!="" and clause.attribute!="Record"):# and not is_datetime_string(clause.attribute): - # if (clause.data_type == ""): - clause.data_type = ldf.data_type_lookup[clause.attribute] + if (clause.data_type == ""): + clause.data_type = ldf.data_type_lookup[clause.attribute] if (clause.data_type=="id"): clause.data_type = "nominal" - # if (clause.data_model == ""): - clause.data_model = ldf.data_model_lookup[clause.attribute] + if (clause.data_model == ""): + clause.data_model = ldf.data_model_lookup[clause.attribute] if (clause.value!=""): if (vis.title == ""): #If user provided title for Vis, then don't override. if(isinstance(clause.value,np.datetime64)): @@ -277,11 +277,12 @@ def line_or_bar(ldf, dimension:Clause, measure:Clause): dimension = d1 color_attr = d2 # Colored Bar/Line chart with Count as default measure - if (nmsr == 0): - vis._inferred_intent.append(count_col) - measure = vis.get_attr_by_data_model("measure")[0] - vis._mark, auto_channel = line_or_bar(ldf, dimension, measure) - auto_channel["color"] = color_attr + if not ldf.pre_aggregated: + if (nmsr == 0 and not ldf.pre_aggregated): + vis._inferred_intent.append(count_col) + measure = vis.get_attr_by_data_model("measure")[0] + vis._mark, auto_channel = line_or_bar(ldf, dimension, measure) + auto_channel["color"] = color_attr elif (ndim == 0 and nmsr == 2): # Scatterplot vis._mark = "scatter" @@ -316,7 +317,6 @@ def line_or_bar(ldf, dimension:Clause, measure:Clause): if (auto_channel!={}): vis = Compiler.enforce_specified_channel(vis, auto_channel) vis._inferred_intent.extend(filters) # add back the preserved filters - @staticmethod def enforce_specified_channel(vis: Vis, auto_channel: Dict[str, str]): """ diff --git a/lux/utils/utils.py b/lux/utils/utils.py index 670c37b7..e28d931b 100644 --- a/lux/utils/utils.py +++ b/lux/utils/utils.py @@ -54,7 +54,7 @@ def check_if_id_like(df,attribute): import re # Strong signals high_cardinality = df.cardinality[attribute]>500 # so that aggregated reset_index fields don't get misclassified - attribute_contain_id = re.search(r'id',attribute) is not None + attribute_contain_id = re.search(r'id',str(attribute)) is not None almost_all_vals_unique = df.cardinality[attribute] >=0.98* len(df) is_string = pd.api.types.is_string_dtype(df[attribute]) if (is_string): diff --git a/tests/test_maintainence.py b/tests/test_maintainence.py index a3e4da99..797d3462 100644 --- a/tests/test_maintainence.py +++ b/tests/test_maintainence.py @@ -51,10 +51,16 @@ def test_metadata_new_df_operation(): def test_metadata_column_group_reset_df(): df = pd.read_csv("lux/data/car.csv") assert not hasattr(df,"_metadata_fresh") + df['Year'] = pd.to_datetime(df['Year'], format='%Y') + assert hasattr(df,"_metadata_fresh") result = df.groupby("Cylinders").mean() assert not hasattr(result,"_metadata_fresh") result._repr_html_() # Note that this should trigger two compute metadata (one for df, and one for an intermediate df.reset_index used to feed inside created Vis) assert result._metadata_fresh==True, "Failed to maintain metadata after display df" + + colgroup_recs = result.recommendation["Column Groups"] + assert len(colgroup_recs) == 5 + for rec in colgroup_recs: assert rec.mark=="bar", "Column Group not displaying bar charts" def test_recs_inplace_operation(): df = pd.read_csv("lux/data/car.csv") diff --git a/tests/test_pandas_coverage.py b/tests/test_pandas_coverage.py index 88d117d7..c4d47616 100644 --- a/tests/test_pandas_coverage.py +++ b/tests/test_pandas_coverage.py @@ -125,15 +125,14 @@ def test_cut(): df = pd.read_csv("lux/data/car.csv") df["Weight"] = pd.cut(df["Weight"], bins = [0, 2500, 7500, 10000], labels = ["small", "medium", "large"]) df._repr_html_() -# def test_groupby_agg_very_small(): +def test_groupby_agg_very_small(): -# url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' -# df = pd.read_csv(url) -# df["Year"] = pd.to_datetime(df["Year"], format='%Y') -# new_df = df.groupby("Origin").agg(sum).reset_index() -# new_df._repr_html_() -# assert list(new_df.recommendation.keys() ) == ['Column Groups'] -# assert len(new_df.cardinality) == 7 + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime(df["Year"], format='%Y') + new_df = df.groupby("Origin").agg(sum).reset_index() + new_df._repr_html_() + assert list(new_df.recommendation.keys() ) == ['Column Groups'] + assert len(new_df.cardinality) == 7 # def test_groupby_multi_index(): # url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' @@ -355,39 +354,37 @@ def compare_vis(vis1, vis2): # Series Tests # ################ -# TODO: These will all fail right now since LuxSeries isn't implemented yet -# def test_df_to_series(): -# # Ensure metadata is kept when going from df to series -# url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' -# df = pd.read_csv(url) -# df._repr_html_() # compute metadata -# assert df.cardinality is not None -# series = df["Weight"] -# assert isinstance(series,lux.core.series.LuxSeries), "Derived series is type LuxSeries." -# assert df["Weight"]._metadata == ['name','_intent', 'data_type_lookup', 'data_type', 'data_model_lookup', 'data_model', 'unique_values', 'cardinality', 'min_max', 'plot_config', '_current_vis', '_widget', '_recommendation'], "Metadata is lost when going from Dataframe to Series." -# assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." -# assert series.name == "Weight", "Pandas Series original `name` property not retained." - -# def test_value_counts(): -# url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' -# df = pd.read_csv(url) -# df._repr_html_() # compute metadata -# assert df.cardinality is not None -# series = df["Weight"] -# series.value_counts() -# assert isinstance(series,lux.core.series.LuxSeries), "Derived series is type LuxSeries." -# assert df["Weight"]._metadata == ['name','_intent', 'data_type_lookup', 'data_type', 'data_model_lookup', 'data_model', 'unique_values', 'cardinality', 'min_max', 'plot_config', '_current_vis', '_widget', '_recommendation'], "Metadata is lost when going from Dataframe to Series." -# assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." -# assert series.name == "Weight", "Pandas Series original `name` property not retained." - -# def test_str_replace(): -# url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' -# df = pd.read_csv(url) -# df._repr_html_() # compute metadata -# assert df.cardinality is not None -# series = df["Brand"].str.replace("chevrolet", "chevy") -# assert isinstance(series,lux.core.series.LuxSeries), "Derived series is type LuxSeries." -# assert df["Brand"]._metadata == ['name','_intent', 'data_type_lookup', 'data_type', 'data_model_lookup', 'data_model', 'unique_values', 'cardinality', 'min_max', 'plot_config', '_current_vis', '_widget', '_recommendation'], "Metadata is lost when going from Dataframe to Series." -# assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." -# assert series.name == "Brand", "Pandas Series original `name` property not retained." +def test_df_to_series(): + # Ensure metadata is kept when going from df to series + df = pd.read_csv("lux/data/car.csv") + df._repr_html_() # compute metadata + assert df.cardinality is not None + series = df["Weight"] + assert isinstance(series,lux.core.series.LuxSeries), "Derived series is type LuxSeries." + df["Weight"]._metadata + assert df["Weight"]._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'], "Metadata is lost when going from Dataframe to Series." + assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." + assert series.name == "Weight", "Pandas Series original `name` property not retained." + +def test_value_counts(): + df = pd.read_csv("lux/data/car.csv") + df._repr_html_() # compute metadata + assert df.cardinality is not None + series = df["Weight"] + series.value_counts() + assert isinstance(series,lux.core.series.LuxSeries), "Derived series is type LuxSeries." + assert df["Weight"]._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'], "Metadata is lost when going from Dataframe to Series." + assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." + assert series.name == "Weight", "Pandas Series original `name` property not retained." + +def test_str_replace(): + url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' + df = pd.read_csv(url) + df._repr_html_() # compute metadata + assert df.cardinality is not None + series = df["Brand"].str.replace("chevrolet", "chevy") + assert isinstance(series,lux.core.series.LuxSeries), "Derived series is type LuxSeries." + assert df["Brand"]._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'], "Metadata is lost when going from Dataframe to Series." + assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." + assert series.name == "Brand", "Pandas Series original `name` property not retained." From f986c76cada21f777738c769af4c096b7b22179c Mon Sep 17 00:00:00 2001 From: jinimukh <46768380+jinimukh@users.noreply.github.com> Date: Sun, 1 Nov 2020 23:04:07 -0800 Subject: [PATCH 033/114] add black to travis (#127) * add black to travis * reformat all code and adjust test * remove .idea * fix contributing doc * small change in contributing * update * reformat, update command to fix version * remove dev dependencies --- .travis.yml | 1 + CONTRIBUTING.md | 5 +- doc/conf.py | 50 +- lux/__init__.py | 2 +- lux/_config/config.py | 269 ++-- lux/_version.py | 2 +- lux/action/__init__.py | 3 +- lux/action/column_group.py | 65 +- lux/action/correlation.py | 116 +- lux/action/custom.py | 34 +- lux/action/enhance.py | 96 +- lux/action/filter.py | 175 +-- lux/action/generalize.py | 138 ++- lux/action/row_group.py | 57 +- lux/action/similarity.py | 90 +- lux/action/univariate.py | 123 +- lux/core/__init__.py | 11 +- lux/core/frame.py | 1583 +++++++++++++----------- lux/core/series.py | 63 +- lux/executor/Executor.py | 15 +- lux/executor/PandasExecutor.py | 383 +++--- lux/executor/SQLExecutor.py | 189 ++- lux/executor/__init__.py | 3 +- lux/history/__init__.py | 3 +- lux/history/event.py | 31 +- lux/history/history.py | 53 +- lux/interestingness/__init__.py | 3 +- lux/interestingness/interestingness.py | 573 +++++---- lux/processor/Compiler.py | 868 +++++++------ lux/processor/Parser.py | 185 +-- lux/processor/Validator.py | 128 +- lux/processor/__init__.py | 3 +- lux/utils/__init__.py | 3 +- lux/utils/date_utils.py | 220 ++-- lux/utils/message.py | 26 +- lux/utils/utils.py | 116 +- lux/vis/Clause.py | 240 ++-- lux/vis/Vis.py | 578 +++++---- lux/vis/VisList.py | 603 +++++---- lux/vis/__init__.py | 4 +- lux/vislib/__init__.py | 3 +- lux/vislib/altair/AltairChart.py | 161 ++- lux/vislib/altair/AltairRenderer.py | 185 +-- lux/vislib/altair/BarChart.py | 184 +-- lux/vislib/altair/Heatmap.py | 101 +- lux/vislib/altair/Histogram.py | 121 +- lux/vislib/altair/LineChart.py | 95 +- lux/vislib/altair/ScatterChart.py | 96 +- lux/vislib/altair/__init__.py | 3 +- requirements-dev.txt | 1 + requirements.txt | 5 +- setup.py | 50 +- tests/__init__.py | 3 +- tests/context.py | 7 +- tests/test_action.py | 243 ++-- tests/test_compiler.py | 458 ++++--- tests/test_config.py | 230 ++-- tests/test_dates.py | 127 +- tests/test_display.py | 17 +- tests/test_error_warning.py | 38 +- tests/test_executor.py | 202 ++- tests/test_interestingness.py | 232 ++-- tests/test_maintainence.py | 71 +- tests/test_nan.py | 16 +- tests/test_pandas.py | 17 +- tests/test_pandas_coverage.py | 410 ++++-- tests/test_parser.py | 105 +- tests/test_performance.py | 65 +- tests/test_type.py | 168 +-- tests/test_vis.py | 205 ++- 70 files changed, 6253 insertions(+), 4476 deletions(-) diff --git a/.travis.yml b/.travis.yml index fcd12c05..98bde1cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ install: - pip install git+https://github.com/lux-org/lux-widget # command to run tests script: + - black --target-version py37 --check . - python -m pytest tests/*.py - pytest --cov-report term --cov=lux tests/ after_success: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8f068c4f..ac05767b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,9 +45,12 @@ You can run them locally to make sure that your changes are working and do not b python -m pytest tests/*.py ``` +# Code Formatting +In order to keep our codebase clean and readible, we are using PEP8 guidelines. To help us maintain and check code style, we are using [black](https://github.com/psf/black). Simply run `black .` before commiting. Failure to do so may fail the tests run on Travis. This package should have been installed for you. + # Submitting a Pull Request -You can commit your code and push to your forked repo. Once all of your local changes have been tested and is working, you are ready to submit a PR. For Lux, we use the "Squash and Merge" strategy to merge in PR, which means that even if you make a lot of small commits in your PR, they will all get squashed into a single commit associated with the PR. Please make sure that comments and unnecessary file changes are not committed as part of the PR by looking at the "File Changes" diff view on the pull request page. + You can commit your code and push to your forked repo. Once all of your local changes have been tested and formatted, you are ready to submit a PR. For Lux, we use the "Squash and Merge" strategy to merge in PR, which means that even if you make a lot of small commits in your PR, they will all get squashed into a single commit associated with the PR. Please make sure that comments and unnecessary file changes are not committed as part of the PR by looking at the "File Changes" diff view on the pull request page. Once the pull request is submitted, the maintainer will get notified and review your pull request. They may ask for additional changes or comment on the PR. You can always make updates to your pull request after submitting it. diff --git a/doc/conf.py b/doc/conf.py index 5983aca3..03862439 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,5 +1,5 @@ # 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 @@ -19,7 +19,8 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html import subprocess -subprocess.call(['sh', './docbuild.sh']) + +subprocess.call(["sh", "./docbuild.sh"]) # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, @@ -28,18 +29,19 @@ # import os import sys -sys.path.insert(0, os.path.abspath('..')) -sys.path.insert(0, os.path.abspath('..')) + +sys.path.insert(0, os.path.abspath("..")) +sys.path.insert(0, os.path.abspath("..")) # -- Project information ----------------------------------------------------- -project = 'Lux' -copyright = '2020, Doris Jung-Lin Lee' -author = 'Doris Jung-Lin Lee' +project = "Lux" +copyright = "2020, Doris Jung-Lin Lee" +author = "Doris Jung-Lin Lee" # The full version, including alpha/beta/rc tags -release = '0.1.2' +release = "0.1.2" # -- General configuration --------------------------------------------------- @@ -48,32 +50,32 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.coverage', - 'sphinx.ext.autosummary', - 'sphinx.ext.doctest', - 'sphinx.ext.githubpages', - 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode', - 'sphinx.ext.napoleon', - 'sphinx.ext.mathjax', - 'sphinx_automodapi.automodapi', - 'sphinx_automodapi.automodsumm' + "sphinx.ext.autodoc", + "sphinx.ext.coverage", + "sphinx.ext.autosummary", + "sphinx.ext.doctest", + "sphinx.ext.githubpages", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", + "sphinx.ext.mathjax", + "sphinx_automodapi.automodapi", + "sphinx_automodapi.automodsumm", ] -autodoc_default_flags = ['members', "inherited-members"] +autodoc_default_flags = ["members", "inherited-members"] autodoc_member_order = "groupwise" autosummary_generate = True numpydoc_show_class_members = False # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # -- Options for HTML output ------------------------------------------------- @@ -88,7 +90,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] html_logo = "_static/logo.png" html_theme_options = {"style_nav_header_background": "#19177c"} @@ -97,4 +99,4 @@ # further. For a list of options available for each theme, see the # documentation. # -master_doc = 'index' +master_doc = "index" diff --git a/lux/__init__.py b/lux/__init__.py index 8688b3d2..92d39840 100644 --- a/lux/__init__.py +++ b/lux/__init__.py @@ -1,5 +1,5 @@ # 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 diff --git a/lux/_config/config.py b/lux/_config/config.py index 12a7271b..0c1e967f 100644 --- a/lux/_config/config.py +++ b/lux/_config/config.py @@ -1,7 +1,7 @@ -''' +""" 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 import warnings @@ -14,159 +14,170 @@ update_actions: Dict[str, bool] = {} update_actions["flag"] = False + class OptionError(AttributeError, KeyError): """ Exception for pandas.options, backwards compatible with KeyError checks """ - + + def _get_action(pat: str, silent: bool = False): - return _registered_actions[pat] + 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 __getattr__(self, name: str): - """ - 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 += "." - prefix += name - try: - v = object.__getattribute__(self, "d")[name] - 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): - """ - Gathers all currently registered actions in a list of DictWrapper - - Return - ------- - List of DictWrapper objects that are registered - """ - l = [] - for name in self.__dir__(): - l.append(self.__getattr__(name)) - return l - - def __len__(self): - return len(list(self.d.keys())) - - def __dir__(self) -> Iterable[str]: - return list(self.d.keys()) + 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 __getattr__(self, name: str): + """ + 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 += "." + prefix += name + try: + v = object.__getattribute__(self, "d")[name] + 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): + """ + Gathers all currently registered actions in a list of DictWrapper + + Return + ------- + List of DictWrapper objects that are registered + """ + l = [] + for name in self.__dir__(): + l.append(self.__getattr__(name)) + 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( - name: str = "", + name: str = "", action: Callable[[Any], Any] = None, display_condition: Optional[Callable[[Any], Any]] = None, *args, ) -> None: - """ - 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) - - 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 + """ + 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) + + 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( - name: str = "", + name: str = "", ) -> None: - """ - 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") - 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") + del _registered_actions[name] + update_actions["flag"] = True - del _registered_actions[name] - update_actions["flag"] = True def is_callable(obj) -> bool: - """ - 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 + """ + 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 + 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, + ) - 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() diff --git a/lux/_version.py b/lux/_version.py index 173cee8a..924b8842 100644 --- a/lux/_version.py +++ b/lux/_version.py @@ -2,4 +2,4 @@ # coding: utf-8 version_info = (0, 2, 0) -__version__ = ".".join(map(str, version_info)) \ No newline at end of file +__version__ = ".".join(map(str, version_info)) diff --git a/lux/action/__init__.py b/lux/action/__init__.py index cbfa9f5b..948becf5 100644 --- a/lux/action/__init__.py +++ b/lux/action/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -11,4 +11,3 @@ # 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. - diff --git a/lux/action/column_group.py b/lux/action/column_group.py index ba1b9e5a..710cea95 100644 --- a/lux/action/column_group.py +++ b/lux/action/column_group.py @@ -1,5 +1,5 @@ # 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 @@ -21,27 +21,44 @@ from lux.vis.VisList import VisList import pandas as pd + def column_group(ldf): - recommendation = {"action":"Column Groups", - "description":"Shows charts of possible visualizations with respect to the column-wise index."} - collection = [] - ldf_flat = ldf - if isinstance(ldf.columns,pd.DatetimeIndex): - ldf_flat.columns = ldf_flat.columns.format() - ldf_flat = ldf_flat.reset_index() #use a single shared ldf_flat so that metadata doesn't need to be computed for every vis - if (ldf.index.nlevels==1): - if ldf.index.name: - index_column_name = ldf.index.name - else: - index_column_name = "index" - if isinstance(ldf.columns,pd.DatetimeIndex): - ldf.columns = ldf.columns.to_native_types() - for attribute in ldf.columns: - if ldf[attribute].dtype!="object" and (attribute!="index"): - vis = Vis([lux.Clause(index_column_name, data_type = "nominal", data_model = "dimension", aggregation=None), lux.Clause(str(attribute), data_type = "quantitative", aggregation=None)]) - collection.append(vis) - vlst = VisList(collection,ldf_flat) - # Note that we are not computing interestingness score here because we want to preserve the arrangement of the aggregated ldf - - recommendation["collection"] = vlst - return recommendation \ No newline at end of file + recommendation = { + "action": "Column Groups", + "description": "Shows charts of possible visualizations with respect to the column-wise index.", + } + collection = [] + ldf_flat = ldf + if isinstance(ldf.columns, pd.DatetimeIndex): + ldf_flat.columns = ldf_flat.columns.format() + ldf_flat = ( + ldf_flat.reset_index() + ) # use a single shared ldf_flat so that metadata doesn't need to be computed for every vis + if ldf.index.nlevels == 1: + if ldf.index.name: + index_column_name = ldf.index.name + else: + index_column_name = "index" + if isinstance(ldf.columns, pd.DatetimeIndex): + ldf.columns = ldf.columns.to_native_types() + for attribute in ldf.columns: + if ldf[attribute].dtype != "object" and (attribute != "index"): + vis = Vis( + [ + lux.Clause( + index_column_name, + data_type="nominal", + data_model="dimension", + aggregation=None, + ), + lux.Clause( + str(attribute), data_type="quantitative", aggregation=None + ), + ] + ) + collection.append(vis) + vlst = VisList(collection, ldf_flat) + # Note that we are not computing interestingness score here because we want to preserve the arrangement of the aggregated ldf + + recommendation["collection"] = vlst + return recommendation diff --git a/lux/action/correlation.py b/lux/action/correlation.py index b5f23fee..5d51ba01 100644 --- a/lux/action/correlation.py +++ b/lux/action/correlation.py @@ -1,5 +1,5 @@ # 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 @@ -22,60 +22,76 @@ # change ignore_transpose to false for now. def correlation(ldf: LuxDataFrame, ignore_transpose: bool = True): - ''' - Generates bivariate visualizations that represent all pairwise relationships in the data. + """ + Generates bivariate visualizations that represent all pairwise relationships in the data. + + Parameters + ---------- + ldf : LuxDataFrame + LuxDataFrame with underspecified intent. - Parameters - ---------- - ldf : LuxDataFrame - LuxDataFrame with underspecified intent. + ignore_transpose: bool + Boolean flag to ignore pairs of attributes whose transpose are already computed (i.e., {X,Y} will be ignored if {Y,X} is already computed) - ignore_transpose: bool - Boolean flag to ignore pairs of attributes whose transpose are already computed (i.e., {X,Y} will be ignored if {Y,X} is already computed) + Returns + ------- + recommendations : Dict[str,obj] + object with a collection of visualizations that result from the Correlation action. + """ - Returns - ------- - recommendations : Dict[str,obj] - object with a collection of visualizations that result from the Correlation action. - ''' + import numpy as np - import numpy as np - filter_specs = utils.get_filter_specs(ldf._intent) - intent = [lux.Clause("?", data_model="measure"), lux.Clause("?", data_model="measure")] - intent.extend(filter_specs) - vlist = VisList(intent,ldf) - recommendation = {"action": "Correlation", - "description": "Show relationships between two

quantitative

attributes."} - ignore_rec_flag = False - if (len(ldf)<5): # Doesn't make sense to compute correlation if less than 4 data values - ignore_rec_flag = True - # Then use the data populated in the vis list to compute score - for vis in vlist: - measures = vis.get_attr_by_data_model("measure") - if len(measures) < 2: raise ValueError( - f"Can not compute correlation between {[x.attribute for x in ldf.columns]} since less than 2 measure values present.") - msr1 = measures[0].attribute - msr2 = measures[1].attribute + filter_specs = utils.get_filter_specs(ldf._intent) + intent = [ + lux.Clause("?", data_model="measure"), + lux.Clause("?", data_model="measure"), + ] + intent.extend(filter_specs) + vlist = VisList(intent, ldf) + recommendation = { + "action": "Correlation", + "description": "Show relationships between two

quantitative

attributes.", + } + ignore_rec_flag = False + if ( + len(ldf) < 5 + ): # Doesn't make sense to compute correlation if less than 4 data values + ignore_rec_flag = True + # Then use the data populated in the vis list to compute score + for vis in vlist: + measures = vis.get_attr_by_data_model("measure") + if len(measures) < 2: + raise ValueError( + f"Can not compute correlation between {[x.attribute for x in ldf.columns]} since less than 2 measure values present." + ) + msr1 = measures[0].attribute + msr2 = measures[1].attribute - if (ignore_transpose): - check_transpose = check_transpose_not_computed(vlist, msr1, msr2) - else: - check_transpose = True - if (check_transpose): - vis.score = interestingness(vis, ldf) - else: - vis.score = -1 - if (ignore_rec_flag): - recommendation["collection"] = [] - return recommendation - vlist = vlist.topK(15) - recommendation["collection"] = vlist - return recommendation + if ignore_transpose: + check_transpose = check_transpose_not_computed(vlist, msr1, msr2) + else: + check_transpose = True + if check_transpose: + vis.score = interestingness(vis, ldf) + else: + vis.score = -1 + if ignore_rec_flag: + recommendation["collection"] = [] + return recommendation + vlist = vlist.topK(15) + recommendation["collection"] = vlist + return recommendation def check_transpose_not_computed(vlist: VisList, a: str, b: str): - transpose_exist = list(filter(lambda x: (x._inferred_intent[0].attribute == b) and (x._inferred_intent[1].attribute == a), vlist)) - if (len(transpose_exist) > 0): - return transpose_exist[0].score == -1 - else: - return False + transpose_exist = list( + filter( + lambda x: (x._inferred_intent[0].attribute == b) + and (x._inferred_intent[1].attribute == a), + vlist, + ) + ) + if len(transpose_exist) > 0: + return transpose_exist[0].score == -1 + else: + return False diff --git a/lux/action/custom.py b/lux/action/custom.py index 114990e7..c709d34b 100644 --- a/lux/action/custom.py +++ b/lux/action/custom.py @@ -1,5 +1,5 @@ # 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 @@ -18,8 +18,9 @@ from lux.executor.SQLExecutor import SQLExecutor import lux + def custom(ldf): - ''' + """ Generates user-defined vis based on the intent. Parameters @@ -31,22 +32,25 @@ def custom(ldf): ------- recommendations : Dict[str,obj] object with a collection of visualizations that result from the Distribution action. - ''' - recommendation = {"action": "Current Vis", - "description": "Shows the list of visualizations generated based on user specified intent"} + """ + recommendation = { + "action": "Current Vis", + "description": "Shows the list of visualizations generated based on user specified intent", + } recommendation["collection"] = ldf.current_vis vlist = ldf.current_vis PandasExecutor.execute(vlist, ldf) - for vis in vlist: - vis.score = interestingness(vis,ldf) + for vis in vlist: + vis.score = interestingness(vis, ldf) # ldf.clear_intent() vlist.sort(remove_invalid=True) return recommendation + def custom_actions(ldf): - ''' + """ Generates user-defined vis based on globally defined actions. Parameters @@ -58,15 +62,19 @@ def custom_actions(ldf): ------- recommendations : Dict[str,obj] object with a collection of visualizations that were previously registered. - ''' - if (lux.actions.__len__() > 0): + """ + if lux.actions.__len__() > 0: recommendations = [] for action_name in lux.actions.__dir__(): - display_condition = lux.actions.__getattr__(action_name).display_condition - if display_condition is None or (display_condition is not None and display_condition(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).action(ldf, args) + recommendation = lux.actions.__getattr__(action_name).action( + ldf, args + ) else: recommendation = lux.actions.__getattr__(action_name).action(ldf) recommendations.append(recommendation) diff --git a/lux/action/enhance.py b/lux/action/enhance.py index 3ad44d72..ffdc2423 100644 --- a/lux/action/enhance.py +++ b/lux/action/enhance.py @@ -1,5 +1,5 @@ # 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 @@ -17,45 +17,57 @@ from lux.processor.Compiler import Compiler from lux.utils import utils + def enhance(ldf): - ''' - Given a set of vis, generates possible visualizations when an additional attribute is added to the current vis. - - Parameters - ---------- - ldf : lux.core.frame - LuxDataFrame with underspecified intent. - - Returns - ------- - recommendations : Dict[str,obj] - object with a collection of visualizations that result from the Enhance action. - ''' - - filters = utils.get_filter_specs(ldf._intent) - # Collect variables that already exist in the intent - attr_specs = list(filter(lambda x: x.value=="" and x.attribute!="Record", ldf._intent)) - fltr_str = [fltr.attribute+fltr.filter_op+str(fltr.value) for fltr in filters] - attr_str = [clause.attribute for clause in attr_specs] - intended_attrs = '

'+', '.join(attr_str+fltr_str)+'

' - if (len(attr_specs)==1): - recommendation = {"action":"Enhance", - "description":f"Augmenting current {intended_attrs} intent with additional attribute."} - elif(len(attr_specs)==2): - recommendation = {"action":"Enhance", - "description":f"Further breaking down current {intended_attrs} intent by additional attribute."} - elif(len(attr_specs)>2): # if there are too many column attributes, return don't generate Enhance recommendations - recommendation = {"action":"Enhance"} - recommendation["collection"] = [] - return recommendation - intent = ldf._intent.copy() - intent = filters + attr_specs - intent.append("?") - vlist = lux.vis.VisList.VisList(intent,ldf) - - # Then use the data populated in the vis list to compute score - for vis in vlist: vis.score = interestingness(vis,ldf) - - vlist = vlist.topK(15) - recommendation["collection"] = vlist - return recommendation \ No newline at end of file + """ + Given a set of vis, generates possible visualizations when an additional attribute is added to the current vis. + + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent. + + Returns + ------- + recommendations : Dict[str,obj] + object with a collection of visualizations that result from the Enhance action. + """ + + filters = utils.get_filter_specs(ldf._intent) + # Collect variables that already exist in the intent + attr_specs = list( + filter(lambda x: x.value == "" and x.attribute != "Record", ldf._intent) + ) + fltr_str = [fltr.attribute + fltr.filter_op + str(fltr.value) for fltr in filters] + attr_str = [clause.attribute for clause in attr_specs] + intended_attrs = ( + '

' + ", ".join(attr_str + fltr_str) + "

" + ) + if len(attr_specs) == 1: + recommendation = { + "action": "Enhance", + "description": f"Augmenting current {intended_attrs} intent with additional attribute.", + } + elif len(attr_specs) == 2: + recommendation = { + "action": "Enhance", + "description": f"Further breaking down current {intended_attrs} intent by additional attribute.", + } + elif ( + len(attr_specs) > 2 + ): # if there are too many column attributes, return don't generate Enhance recommendations + recommendation = {"action": "Enhance"} + recommendation["collection"] = [] + return recommendation + intent = ldf._intent.copy() + intent = filters + attr_specs + intent.append("?") + vlist = lux.vis.VisList.VisList(intent, ldf) + + # Then use the data populated in the vis list to compute score + for vis in vlist: + vis.score = interestingness(vis, ldf) + + vlist = vlist.topK(15) + recommendation["collection"] = vlist + return recommendation diff --git a/lux/action/filter.py b/lux/action/filter.py index b792260a..f0972722 100644 --- a/lux/action/filter.py +++ b/lux/action/filter.py @@ -1,5 +1,5 @@ # 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 @@ -19,83 +19,104 @@ from lux.processor.Compiler import Compiler from lux.utils import utils + def filter(ldf): - ''' - Iterates over all possible values of a categorical variable and generates visualizations where each categorical value filters the data. + """ + Iterates over all possible values of a categorical variable and generates visualizations where each categorical value filters the data. + + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent. + + Returns + ------- + recommendations : Dict[str,obj] + object with a collection of visualizations that result from the Filter action. + """ + filters = utils.get_filter_specs(ldf._intent) + filter_values = [] + output = [] + # if fltr is specified, create visualizations where data is filtered by all values of the fltr's categorical variable + column_spec = utils.get_attrs_specs(ldf.current_vis[0]._inferred_intent) + column_spec_attr = map(lambda x: x.attribute, column_spec) + if len(filters) == 1: + # get unique values for all categorical values specified and creates corresponding filters + fltr = filters[0] + + if ldf.data_type_lookup[fltr.attribute] == "nominal": + recommendation = { + "action": "Filter", + "description": f"Changing the

{fltr.attribute}

filter to an alternative value.", + } + unique_values = ldf.unique_values[fltr.attribute] + filter_values.append(fltr.value) + # creates vis with new filters + for val in unique_values: + if val not in filter_values: + new_spec = column_spec.copy() + new_filter = lux.Clause(attribute=fltr.attribute, value=val) + new_spec.append(new_filter) + temp_vis = Vis(new_spec) + output.append(temp_vis) + elif ldf.data_type_lookup[fltr.attribute] == "quantitative": + recommendation = { + "action": "Filter", + "description": f"Changing the

{fltr.attribute}

filter to an alternative inequality operation.", + } - Parameters - ---------- - ldf : lux.core.frame - LuxDataFrame with underspecified intent. + def get_complementary_ops(fltr_op): + if fltr_op == ">": + return "<=" + elif fltr_op == "<": + return ">=" + elif fltr_op == ">=": + return "<" + elif fltr_op == "<=": + return ">" + # TODO: need to support case where fltr_op is "=" --> auto-binned ranges - Returns - ------- - recommendations : Dict[str,obj] - object with a collection of visualizations that result from the Filter action. - ''' - filters = utils.get_filter_specs(ldf._intent) - filter_values = [] - output = [] - #if fltr is specified, create visualizations where data is filtered by all values of the fltr's categorical variable - column_spec = utils.get_attrs_specs(ldf.current_vis[0]._inferred_intent) - column_spec_attr = map(lambda x: x.attribute,column_spec) - if len(filters) == 1: - #get unique values for all categorical values specified and creates corresponding filters - fltr = filters[0] - - if (ldf.data_type_lookup[fltr.attribute]=="nominal"): - recommendation = {"action":"Filter", - "description":f"Changing the

{fltr.attribute}

filter to an alternative value."} - unique_values = ldf.unique_values[fltr.attribute] - filter_values.append(fltr.value) - #creates vis with new filters - for val in unique_values: - if val not in filter_values: - new_spec = column_spec.copy() - new_filter = lux.Clause(attribute = fltr.attribute, value = val) - new_spec.append(new_filter) - temp_vis = Vis(new_spec) - output.append(temp_vis) - elif (ldf.data_type_lookup[fltr.attribute]=="quantitative"): - recommendation = {"action":"Filter", - "description":f"Changing the

{fltr.attribute}

filter to an alternative inequality operation."} - def get_complementary_ops(fltr_op): - if (fltr_op=='>'): - return '<=' - elif (fltr_op=='<'): - return '>=' - elif (fltr_op=='>='): - return '<' - elif (fltr_op=='<='): - return '>' - # TODO: need to support case where fltr_op is "=" --> auto-binned ranges - # Create vis with complementary filter operations - new_spec = column_spec.copy() - new_filter = lux.Clause(attribute = fltr.attribute, filter_op=get_complementary_ops(fltr.filter_op),value = fltr.value) - new_spec.append(new_filter) - temp_vis = Vis(new_spec,score=1) - output.append(temp_vis) + # Create vis with complementary filter operations + new_spec = column_spec.copy() + new_filter = lux.Clause( + attribute=fltr.attribute, + filter_op=get_complementary_ops(fltr.filter_op), + value=fltr.value, + ) + new_spec.append(new_filter) + temp_vis = Vis(new_spec, score=1) + output.append(temp_vis) - else: #if no existing filters, create filters using unique values from all categorical variables in the dataset - intended_attrs = ', '.join([clause.attribute for clause in ldf._intent if clause.value=='' and clause.attribute!="Record"]) - recommendation = {"action":"Filter", - "description":f"Applying filters to the

{intended_attrs}

intent."} - categorical_vars = [] - for col in list(ldf.columns): - # if cardinality is not too high, and attribute is not one of the X,Y (specified) column - if ldf.cardinality[col]<30 and col not in column_spec_attr: - categorical_vars.append(col) - for cat in categorical_vars: - unique_values = ldf.unique_values[cat] - for i in range(0, len(unique_values)): - new_spec = column_spec.copy() - new_filter = lux.Clause(attribute=cat, filter_op="=",value=unique_values[i]) - new_spec.append(new_filter) - temp_vis = Vis(new_spec) - output.append(temp_vis) - vlist = lux.vis.VisList.VisList(output,ldf) - for vis in vlist: - vis.score = interestingness(vis,ldf) - vlist = vlist.topK(15) - recommendation["collection"] = vlist - return recommendation \ No newline at end of file + else: # if no existing filters, create filters using unique values from all categorical variables in the dataset + intended_attrs = ", ".join( + [ + clause.attribute + for clause in ldf._intent + if clause.value == "" and clause.attribute != "Record" + ] + ) + recommendation = { + "action": "Filter", + "description": f"Applying filters to the

{intended_attrs}

intent.", + } + categorical_vars = [] + for col in list(ldf.columns): + # if cardinality is not too high, and attribute is not one of the X,Y (specified) column + if ldf.cardinality[col] < 30 and col not in column_spec_attr: + categorical_vars.append(col) + for cat in categorical_vars: + unique_values = ldf.unique_values[cat] + for i in range(0, len(unique_values)): + new_spec = column_spec.copy() + new_filter = lux.Clause( + attribute=cat, filter_op="=", value=unique_values[i] + ) + new_spec.append(new_filter) + temp_vis = Vis(new_spec) + output.append(temp_vis) + vlist = lux.vis.VisList.VisList(output, ldf) + for vis in vlist: + vis.score = interestingness(vis, ldf) + vlist = vlist.topK(15) + recommendation["collection"] = vlist + return recommendation diff --git a/lux/action/generalize.py b/lux/action/generalize.py index 1588907c..c6096cc0 100644 --- a/lux/action/generalize.py +++ b/lux/action/generalize.py @@ -1,5 +1,5 @@ # 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 @@ -18,72 +18,84 @@ from lux.utils import utils from lux.interestingness.interestingness import interestingness + def generalize(ldf): - ''' - Generates all possible visualizations when one attribute or filter from the current vis is removed. + """ + Generates all possible visualizations when one attribute or filter from the current vis is removed. + + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent. - Parameters - ---------- - ldf : lux.core.frame - LuxDataFrame with underspecified intent. + Returns + ------- + recommendations : Dict[str,obj] + object with a collection of visualizations that result from the Generalize action. + """ + # takes in a dataObject and generates a list of new dataObjects, each with a single measure from the original object removed + # --> return list of dataObjects with corresponding interestingness scores - Returns - ------- - recommendations : Dict[str,obj] - object with a collection of visualizations that result from the Generalize action. - ''' - # takes in a dataObject and generates a list of new dataObjects, each with a single measure from the original object removed - # --> return list of dataObjects with corresponding interestingness scores + output = [] + excluded_columns = [] + attributes = list( + filter(lambda x: x.value == "" and x.attribute != "Record", ldf._intent) + ) + filters = utils.get_filter_specs(ldf._intent) - output = [] - excluded_columns = [] - attributes = list(filter(lambda x: x.value=="" and x.attribute!="Record", ldf._intent)) - filters = utils.get_filter_specs(ldf._intent) + fltr_str = [fltr.attribute + fltr.filter_op + str(fltr.value) for fltr in filters] + attr_str = [clause.attribute for clause in attributes] + intended_attrs = ( + '

' + ", ".join(attr_str + fltr_str) + "

" + ) - fltr_str = [fltr.attribute+fltr.filter_op+str(fltr.value) for fltr in filters] - attr_str = [clause.attribute for clause in attributes] - intended_attrs = '

'+', '.join(attr_str+fltr_str)+'

' + recommendation = { + "action": "Generalize", + "description": f"Remove an attribute or filter from {intended_attrs}.", + } + # to observe a more general trend + # if we do no have enough column attributes or too many, return no vis. + if len(attributes) < 1 or len(attributes) > 4: + recommendation["collection"] = [] + return recommendation + # for each column specification, create a copy of the ldf's vis and remove the column specification + # then append the vis to the output + if len(attributes) > 1: + for clause in attributes: + columns = clause.attribute + if type(columns) == list: + for column in columns: + if column not in excluded_columns: + temp_vis = Vis(ldf.copy_intent(), score=1) + temp_vis.remove_column_from_spec(column, remove_first=True) + excluded_columns.append(column) + output.append(temp_vis) + elif type(columns) == str: + if columns not in excluded_columns: + temp_vis = Vis(ldf.copy_intent(), score=1) + temp_vis.remove_column_from_spec(columns, remove_first=True) + excluded_columns.append(columns) + output.append(temp_vis) + # for each filter specification, create a copy of the ldf's current vis and remove the filter specification, + # then append the vis to the output + for clause in filters: + # new_spec = ldf._intent.copy() + # new_spec.remove_column_from_spec(new_spec.attribute) + temp_vis = Vis( + ldf.current_vis[0]._inferred_intent.copy(), + source=ldf, + title="Overall", + score=0, + ) + temp_vis.remove_filter_from_spec(clause.value) + output.append(temp_vis) - recommendation = {"action":"Generalize", - "description":f"Remove an attribute or filter from {intended_attrs}."} - # to observe a more general trend - # if we do no have enough column attributes or too many, return no vis. - if(len(attributes)<1 or len(attributes)>4): - recommendation["collection"] = [] - return recommendation - #for each column specification, create a copy of the ldf's vis and remove the column specification - #then append the vis to the output - if (len(attributes)>1): - for clause in attributes: - columns = clause.attribute - if type(columns) == list: - for column in columns: - if column not in excluded_columns: - temp_vis = Vis(ldf.copy_intent(),score=1) - temp_vis.remove_column_from_spec(column, remove_first = True) - excluded_columns.append(column) - output.append(temp_vis) - elif type(columns) == str: - if columns not in excluded_columns: - temp_vis = Vis(ldf.copy_intent(),score=1) - temp_vis.remove_column_from_spec(columns, remove_first = True) - excluded_columns.append(columns) - output.append(temp_vis) - #for each filter specification, create a copy of the ldf's current vis and remove the filter specification, - #then append the vis to the output - for clause in filters: - #new_spec = ldf._intent.copy() - #new_spec.remove_column_from_spec(new_spec.attribute) - temp_vis = Vis(ldf.current_vis[0]._inferred_intent.copy(),source = ldf,title="Overall",score=0) - temp_vis.remove_filter_from_spec(clause.value) - output.append(temp_vis) - - vlist = lux.vis.VisList.VisList(output,source=ldf) - # Ignore interestingness sorting since Generalize yields very few vis (preserve order of remove attribute, then remove filters) - # for vis in vlist: - # vis.score = interestingness(vis,ldf) + vlist = lux.vis.VisList.VisList(output, source=ldf) + # Ignore interestingness sorting since Generalize yields very few vis (preserve order of remove attribute, then remove filters) + # for vis in vlist: + # vis.score = interestingness(vis,ldf) - vlist.remove_duplicates() - vlist.sort(remove_invalid=True) - recommendation["collection"] = vlist - return recommendation \ No newline at end of file + vlist.remove_duplicates() + vlist.sort(remove_invalid=True) + recommendation["collection"] = vlist + return recommendation diff --git a/lux/action/row_group.py b/lux/action/row_group.py index a7d688ee..3fca5428 100644 --- a/lux/action/row_group.py +++ b/lux/action/row_group.py @@ -1,5 +1,5 @@ # 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 @@ -21,29 +21,34 @@ from lux.vis.VisList import VisList import pandas as pd + def row_group(ldf): - recommendation = {"action":"Row Groups", - "description":"Shows charts of possible visualizations with respect to the row-wise index."} - collection = [] - - if (ldf.index.nlevels==1): - if (ldf.columns.name is not None): - dim_name = ldf.columns.name - else: - dim_name = "index" - for row_id in range(len(ldf)): - row = ldf.iloc[row_id,] - rowdf = row.reset_index() - # if (dim_name =="index"): #TODO: need to change this to auto-detect - # rowdf.data_type_lookup["index"]="nominal" - # rowdf.data_model_lookup["index"]="dimension" - # rowdf.cardinality["index"]=len(rowdf) - # if isinstance(ldf.columns,pd.DatetimeIndex): - # rowdf.data_type_lookup[dim_name]="temporal" - vis = Vis([dim_name,lux.Clause(row.name,aggregation=None)],rowdf) - collection.append(vis) - vlst = VisList(collection) - # Note that we are not computing interestingness score here because we want to preserve the arrangement of the aggregated data - - recommendation["collection"] = vlst - return recommendation \ No newline at end of file + recommendation = { + "action": "Row Groups", + "description": "Shows charts of possible visualizations with respect to the row-wise index.", + } + collection = [] + + if ldf.index.nlevels == 1: + if ldf.columns.name is not None: + dim_name = ldf.columns.name + else: + dim_name = "index" + for row_id in range(len(ldf)): + row = ldf.iloc[ + row_id, + ] + rowdf = row.reset_index() + # if (dim_name =="index"): #TODO: need to change this to auto-detect + # rowdf.data_type_lookup["index"]="nominal" + # rowdf.data_model_lookup["index"]="dimension" + # rowdf.cardinality["index"]=len(rowdf) + # if isinstance(ldf.columns,pd.DatetimeIndex): + # rowdf.data_type_lookup[dim_name]="temporal" + vis = Vis([dim_name, lux.Clause(row.name, aggregation=None)], rowdf) + collection.append(vis) + vlst = VisList(collection) + # Note that we are not computing interestingness score here because we want to preserve the arrangement of the aggregated data + + recommendation["collection"] = vlst + return recommendation diff --git a/lux/action/similarity.py b/lux/action/similarity.py index 8c369f93..c9871cbc 100644 --- a/lux/action/similarity.py +++ b/lux/action/similarity.py @@ -1,5 +1,5 @@ # 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 @@ -18,14 +18,15 @@ import numpy as np from lux.vis.VisList import VisList + def similar_pattern(ldf, intent, topK=-1): - ''' + """ Generates visualizations with similar patterns to a query visualization. Parameters ---------- ldf : lux.core.frame - LuxDataFrame with underspecified intent. + LuxDataFrame with underspecified intent. intent: list[lux.Clause] intent for specifying the visual query for the similarity search. @@ -36,31 +37,34 @@ def similar_pattern(ldf, intent, topK=-1): Returns ------- recommendations : Dict[str,obj] - object with a collection of visualizations that result from the Similarity action - ''' + object with a collection of visualizations that result from the Similarity action + """ row_specs = list(filter(lambda x: x.value != "", intent)) - if(len(row_specs) == 1): - search_space_vc = VisList(ldf.current_vis.collection.copy(),ldf) + if len(row_specs) == 1: + search_space_vc = VisList(ldf.current_vis.collection.copy(), ldf) - query_vc = VisList(intent,ldf) + query_vc = VisList(intent, ldf) query_vis = query_vc[0] preprocess(query_vis) - #for loop to create assign euclidean distance - recommendation = {"action":"Similarity", - "description":"Show other charts that are visually similar to the Current vis."} + # for loop to create assign euclidean distance + recommendation = { + "action": "Similarity", + "description": "Show other charts that are visually similar to the Current vis.", + } for vis in search_space_vc: preprocess(vis) vis.score = euclidean_dist(query_vis, vis) search_space_vc.normalize_score(invert_order=True) - if(topK!=-1): + if topK != -1: search_space_vc = search_space_vc.topK(topK) recommendation["collection"] = search_space_vc return recommendation else: print("Query needs to have 1 row value") + def aggregate(vis): - ''' + """ Aggregates data values on the y axis so that the vis is a time series Parameters @@ -70,16 +74,22 @@ def aggregate(vis): Returns ------- None - ''' + """ if vis.get_attr_by_channel("x") and vis.get_attr_by_channel("y"): xAxis = vis.get_attr_by_channel("x")[0].attribute yAxis = vis.get_attr_by_channel("y")[0].attribute - vis.data = vis.data[[xAxis,yAxis]].groupby(xAxis,as_index=False).agg({yAxis:'mean'}).copy() + vis.data = ( + vis.data[[xAxis, yAxis]] + .groupby(xAxis, as_index=False) + .agg({yAxis: "mean"}) + .copy() + ) -def interpolate(vis,length): - ''' + +def interpolate(vis, length): + """ Interpolates the vis data so that the number of data points is fixed to a constant Parameters @@ -92,7 +102,7 @@ def interpolate(vis,length): Returns ------- None - ''' + """ if vis.get_attr_by_channel("x") and vis.get_attr_by_channel("y"): xAxis = vis.get_attr_by_channel("x")[0].attribute @@ -103,32 +113,40 @@ def interpolate(vis,length): xVals = vis.data[xAxis] n = length - interpolated_x_vals = [0.0]*(length) - interpolated_y_vals = [0.0]*(length) + interpolated_x_vals = [0.0] * (length) + interpolated_y_vals = [0.0] * (length) - granularity = (xVals[len(xVals)-1] - xVals[0]) / n + granularity = (xVals[len(xVals) - 1] - xVals[0]) / n count = 0 - for i in range(0,n): + for i in range(0, n): interpolated_x = xVals[0] + i * granularity interpolated_x_vals[i] = interpolated_x while xVals[count] < interpolated_x: - if(count < len(xVals)): + if count < len(xVals): count += 1 if xVals[count] == interpolated_x: interpolated_y_vals[i] = yVals[count] else: - x_diff = xVals[count] - xVals[count-1] - yDiff = yVals[count] - yVals[count-1] - interpolated_y_vals[i] = yVals[count-1] + (interpolated_x - xVals[count-1]) / x_diff * yDiff - vis.data = pd.DataFrame(list(zip(interpolated_x_vals, interpolated_y_vals)),columns = [xAxis, yAxis]) + x_diff = xVals[count] - xVals[count - 1] + yDiff = yVals[count] - yVals[count - 1] + interpolated_y_vals[i] = ( + yVals[count - 1] + + (interpolated_x - xVals[count - 1]) / x_diff * yDiff + ) + vis.data = pd.DataFrame( + list(zip(interpolated_x_vals, interpolated_y_vals)), + columns=[xAxis, yAxis], + ) + # interpolate dataset + def normalize(vis): - ''' + """ Normalizes the vis data so that the range of values is 0 to 1 for the vis Parameters @@ -138,17 +156,18 @@ def normalize(vis): Returns ------- None - ''' + """ if vis.get_attr_by_channel("y"): y_axis = vis.get_attr_by_channel("y")[0].attribute max = vis.data[y_axis].max() min = vis.data[y_axis].min() - if(max == min or (max-min<1)): + if max == min or (max - min < 1): return vis.data[y_axis] = (vis.data[y_axis] - min) / (max - min) + def euclidean_dist(query_vis, vis): - ''' + """ Calculates euclidean distance score for similarity between two visualizations Parameters @@ -162,7 +181,7 @@ def euclidean_dist(query_vis, vis): ------- score : float euclidean distance score - ''' + """ if query_vis.get_attr_by_channel("y") and vis.get_attr_by_channel("y"): @@ -177,8 +196,10 @@ def euclidean_dist(query_vis, vis): else: print("no y axis detected") return 0 + + def preprocess(vis): - ''' + """ Processes vis data to allow similarity comparisons between visualizations Parameters @@ -188,8 +209,7 @@ def preprocess(vis): Returns ------- None - ''' + """ aggregate(vis) interpolate(vis, 100) normalize(vis) - diff --git a/lux/action/univariate.py b/lux/action/univariate.py index 0d58c8bf..4eb0157e 100644 --- a/lux/action/univariate.py +++ b/lux/action/univariate.py @@ -1,5 +1,5 @@ # 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 @@ -16,60 +16,77 @@ from lux.vis.VisList import VisList import lux from lux.utils import utils + + def univariate(ldf, *args): - ''' - Generates bar chart distributions of different attributes in the dataframe. + """ + Generates bar chart distributions of different attributes in the dataframe. + + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent. - Parameters - ---------- - ldf : lux.core.frame - LuxDataFrame with underspecified intent. + data_type_constraint: str + Controls the type of distribution chart that will be rendered. - data_type_constraint: str - Controls the type of distribution chart that will be rendered. + Returns + ------- + recommendations : Dict[str,obj] + object with a collection of visualizations that result from the Distribution action. + """ + import numpy as np - Returns - ------- - recommendations : Dict[str,obj] - 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] + 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 - if (data_type_constraint== "quantitative"): - possible_attributes = [c for c in ldf.columns if ldf.data_type_lookup[c] == "quantitative" - and ldf.cardinality[c] > 5 - and c !="Number of Records"] - intent = [lux.Clause(possible_attributes)] - intent.extend(filter_specs) - recommendation = {"action":"Distribution", - "description":"Show univariate histograms of

quantitative

attributes."} - if (len(ldf)<5): # Doesn't make sense to generate a histogram if there is less than 5 datapoints (pre-aggregated) - ignore_rec_flag = True - elif (data_type_constraint == "nominal"): - intent = [lux.Clause("?",data_type="nominal")] - intent.extend(filter_specs) - recommendation = {"action":"Occurrence", - "description":"Show frequency of occurrence for

categorical

attributes."} - elif (data_type_constraint == "temporal"): - intent = [lux.Clause("?",data_type="temporal")] - intent.extend(filter_specs) - recommendation = {"action":"Temporal", - "description":"Show trends over

time-related

attributes."} - if (len(ldf)<3): # Doesn't make sense to generate a line chart if there is less than 3 datapoints (pre-aggregated) - ignore_rec_flag = True - if (ignore_rec_flag): - recommendation["collection"] = [] - return recommendation - vlist = VisList(intent,ldf) - for vis in vlist: - vis.score = interestingness(vis,ldf) - # vlist = vlist.topK(15) # Basic visualizations should not be capped - vlist.sort() - recommendation["collection"] = vlist - return recommendation \ No newline at end of file + filter_specs = utils.get_filter_specs(ldf._intent) + ignore_rec_flag = False + if data_type_constraint == "quantitative": + possible_attributes = [ + c + for c in ldf.columns + if ldf.data_type_lookup[c] == "quantitative" + and ldf.cardinality[c] > 5 + and c != "Number of Records" + ] + intent = [lux.Clause(possible_attributes)] + intent.extend(filter_specs) + recommendation = { + "action": "Distribution", + "description": "Show univariate histograms of

quantitative

attributes.", + } + if ( + len(ldf) < 5 + ): # Doesn't make sense to generate a histogram if there is less than 5 datapoints (pre-aggregated) + ignore_rec_flag = True + elif data_type_constraint == "nominal": + intent = [lux.Clause("?", data_type="nominal")] + intent.extend(filter_specs) + recommendation = { + "action": "Occurrence", + "description": "Show frequency of occurrence for

categorical

attributes.", + } + elif data_type_constraint == "temporal": + intent = [lux.Clause("?", data_type="temporal")] + intent.extend(filter_specs) + recommendation = { + "action": "Temporal", + "description": "Show trends over

time-related

attributes.", + } + if ( + len(ldf) < 3 + ): # Doesn't make sense to generate a line chart if there is less than 3 datapoints (pre-aggregated) + ignore_rec_flag = True + if ignore_rec_flag: + recommendation["collection"] = [] + return recommendation + vlist = VisList(intent, ldf) + for vis in vlist: + vis.score = interestingness(vis, ldf) + # vlist = vlist.topK(15) # Basic visualizations should not be capped + vlist.sort() + recommendation["collection"] = vlist + return recommendation diff --git a/lux/core/__init__.py b/lux/core/__init__.py index 771a533f..2d72ffb8 100644 --- a/lux/core/__init__.py +++ b/lux/core/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -14,14 +14,17 @@ import pandas as pd from .frame import LuxDataFrame -global originalDF; + +global originalDF # Keep variable scope of original pandas df originalDF = pd.core.frame.DataFrame + def setOption(overridePandas=True): - if (overridePandas): + if overridePandas: pd.DataFrame = pd.io.parsers.DataFrame = pd.core.frame.DataFrame = LuxDataFrame else: pd.DataFrame = pd.io.parsers.DataFrame = pd.core.frame.DataFrame = originalDF -setOption(overridePandas=True) \ No newline at end of file + +setOption(overridePandas=True) diff --git a/lux/core/frame.py b/lux/core/frame.py index bd7cea6a..6e45621b 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -1,5 +1,5 @@ # 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 @@ -23,715 +23,874 @@ 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. - ''' - # 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'] - - 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) - - self.executor_type = "Pandas" - self.executor = PandasExecutor() - self.SQLconnection = "" - self.table_name = "" - - self._sampled = None - self._default_pandas_display = True - self._toggle_pandas_display = True - self._plot_config = None - self._message = Message() - self._pandas_only=False - # Metadata - self.data_type_lookup = None - self.data_type = None - self.data_model_lookup = None - self.data_model = None - self.unique_values = None - self.cardinality = None - self._min_max = None - self.pre_aggregated = None - - @property - def _constructor(self): - return LuxDataFrame - @property - def _constructor_sliced(self): - def f(*args, **kwargs): - s = LuxSeries(*args, **kwargs) - for attr in self._metadata: #propagate metadata - s.__dict__[attr] = getattr(self, attr, None) - return s - return f - - @property - def history(self): - return self._history - def maintain_metadata(self): - if (not hasattr(self,"_metadata_fresh") or not self._metadata_fresh ): # Check that metadata has not yet been computed - if (len(self)>0): #only compute metadata information if the dataframe is non-empty - self.executor.compute_stats(self) - self.executor.compute_dataset_metadata(self) - self._infer_structure() - self._metadata_fresh = True - def expire_recs(self): - self._recs_fresh = False - self.recommendation = {} - self.current_vis = None - self._widget = None - self._rec_info = None - self._sampled = None - def expire_metadata(self): - # Set metadata as null - self._metadata_fresh = False - self.data_type_lookup = None - self.data_type = None - self.data_model_lookup = None - self.data_model = None - self.unique_values = None - self.cardinality = None - self._min_max = None - self.pre_aggregated = None - - ##################### - ## Override Pandas ## - ##################### - def __getattr__(self, name): - ret_value = super(LuxDataFrame, self).__getattr__(name) - self.expire_metadata() - self.expire_recs() - return ret_value - def _set_axis(self, axis, labels): - super(LuxDataFrame, self)._set_axis(axis, labels) - self.expire_metadata() - self.expire_recs() - def _update_inplace(self,*args,**kwargs): - super(LuxDataFrame, self)._update_inplace(*args,**kwargs) - self.expire_metadata() - self.expire_recs() - def _set_item(self, key, value): - super(LuxDataFrame, self)._set_item(key, value) - self.expire_metadata() - self.expire_recs() - 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 - not_int_index_flag = self.index.dtype !='int64' - small_df_flag = len(self)<100 - self.pre_aggregated = (is_multi_index_flag or not_int_index_flag) and small_df_flag - if ("Number of Records" in self.columns): - self.pre_aggregated = True - very_small_df_flag = len(self)<=10 - if (very_small_df_flag): - self.pre_aggregated = True - def set_executor_type(self, exe): - if (exe =="SQL"): - import pkgutil - if (pkgutil.find_loader("psycopg2") is None): - raise ImportError("psycopg2 is not installed. Run `pip install psycopg2' to install psycopg2 to enable the Postgres connection.") - else: - import psycopg2 - from lux.executor.SQLExecutor import SQLExecutor - self.executor = SQLExecutor - else: - from lux.executor.PandasExecutor import PandasExecutor - self.executor = PandasExecutor() - self.executor_type = exe - @property - def plot_config(self): - return self._plot_config - @plot_config.setter - def plot_config(self,config_func:Callable): - """ - Modify plot aesthetic settings to all visualizations in the dataframe display - Currently only supported for Altair visualizations - Parameters - ---------- - config_func : Callable - A function that takes in an AltairChart (https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html) as input and returns an AltairChart as output - - Example - ---------- - Changing the color of marks and adding a title for all charts displayed for this dataframe - >>> df = pd.read_csv("lux/data/car.csv") - >>> def changeColorAddTitle(chart): - chart = chart.configure_mark(color="red") # change mark color to red - chart.title = "Custom Title" # add title to chart - return chart - >>> df.plot_config = changeColorAddTitle - >>> df - Change the opacity of all scatterplots displayed for this dataframe - >>> df = pd.read_csv("lux/data/olympic.csv") - >>> def changeOpacityScatterOnly(chart): - if chart.mark=='circle': - chart = chart.configure_mark(opacity=0.1) # lower opacity - return chart - >>> df.plot_config = changeOpacityScatterOnly - >>> df - """ - self._plot_config = config_func - self._recs_fresh=False - def clear_plot_config(self): - self._plot_config = None - self._recs_fresh=False - - @property - def intent(self): - return self._intent - @intent.setter - def intent(self, intent_input:Union[List[Union[str, Clause]],Vis]): - is_list_input = isinstance(intent_input,list) - is_vis_input = isinstance(intent_input,Vis) - if not (is_list_input or is_vis_input): - raise TypeError("Input intent must be either a list (of strings or lux.Clause) or a lux.Vis object." - "\nSee more at: https://lux-api.readthedocs.io/en/latest/source/guide/intent.html" - ) - if is_list_input: - self.set_intent(intent_input) - elif is_vis_input: - self.set_intent_as_vis(intent_input) - def clear_intent(self): - self.intent = [] - 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` - """ - self.expire_recs() - self._intent = intent - self._parse_validate_compile_intent() - def _parse_validate_compile_intent(self): - from lux.processor.Parser import Parser - from lux.processor.Validator import Validator - self._intent = Parser.parse(self._intent) - Validator.validate_intent(self._intent,self) - self.maintain_metadata() - from lux.processor.Compiler import Compiler - self.current_vis = Compiler.compile_intent(self, self._intent) - - def copy_intent(self): - #creates a true copy of the dataframe's intent - output = [] - for clause in self._intent: - temp_clause = clause.copy_clause() - output.append(temp_clause) - return(output) - - def set_intent_as_vis(self,vis:Vis): - """ - Set intent of the dataframe as the Vis - - Parameters - ---------- - vis : Vis - """ - self.expire_recs() - self._intent = vis._inferred_intent - self._parse_validate_compile_intent() - - def to_pandas(self): - import lux.core - return lux.core.originalDF(self,copy=False) - - @property - def recommendation(self): - return self._recommendation - @recommendation.setter - def recommendation(self,recommendation:Dict): - self._recommendation = recommendation - @property - def current_vis(self): - return self._current_vis - @current_vis.setter - def current_vis(self,current_vis:Dict): - self._current_vis = current_vis - def __repr__(self): - # TODO: _repr_ gets called from _repr_html, need to get rid of this call - return "" - - ####################################################### - ########## SQL Metadata, type, model schema ########### - ####################################################### - - def set_SQL_connection(self, connection, t_name): - self.SQLconnection = connection - self.table_name = t_name - self.compute_SQL_dataset_metadata() - self.set_executor_type("SQL") - - def compute_SQL_dataset_metadata(self): - self.get_SQL_attributes() - for attr in list(self.columns): - self[attr] = None - self.data_type_lookup = {} - self.data_type = {} - #####NOTE: since we aren't expecting users to do much data processing with the SQL database, should we just keep this - ##### in the initialization and do it just once - self.compute_SQL_data_type() - self.compute_SQL_stats() - self.data_model_lookup = {} - self.data_model = {} - self.compute_data_model() - - def compute_SQL_stats(self): - # precompute statistics - self.unique_values = {} - self._min_max = {} - - self.get_SQL_unique_values() - #self.get_SQL_cardinality() - for attribute in self.columns: - if self.data_type_lookup[attribute] == 'quantitative': - self._min_max[attribute] = (self[attribute].min(), self[attribute].max()) - - def get_SQL_attributes(self): - if "." in self.table_name: - table_name = self.table_name[self.table_name.index(".")+1:] - else: - table_name = self.table_name - attr_query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '{}'".format(table_name) - attributes = list(pd.read_sql(attr_query, self.SQLconnection)['column_name']) - for attr in attributes: - self[attr] = None - - def get_SQL_cardinality(self): - cardinality = {} - for attr in list(self.columns): - card_query = pd.read_sql("SELECT Count(Distinct({})) FROM {}".format(attr, self.table_name), self.SQLconnection) - cardinality[attr] = list(card_query["count"])[0] - self.cardinality = cardinality - - def get_SQL_unique_values(self): - unique_vals = {} - for attr in list(self.columns): - unique_query = pd.read_sql("SELECT Distinct({}) FROM {}".format(attr, self.table_name), self.SQLconnection) - unique_vals[attr] = list(unique_query[attr]) - self.unique_values = unique_vals - - def compute_SQL_data_type(self): - data_type_lookup = {} - sql_dtypes = {} - self.get_SQL_cardinality() - if "." in self.table_name: - table_name = self.table_name[self.table_name.index(".")+1:] - else: - table_name = self.table_name - #get the data types of the attributes in the SQL table - for attr in list(self.columns): - datatype_query = "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{}' AND COLUMN_NAME = '{}'".format(table_name, attr) - datatype = list(pd.read_sql(datatype_query, self.SQLconnection)['data_type'])[0] - sql_dtypes[attr] = datatype - - data_type = {"quantitative":[], "nominal":[], "temporal":[]} - for attr in list(self.columns): - if str(attr).lower() in ["month", "year"]: - data_type_lookup[attr] = "temporal" - data_type["temporal"].append(attr) - elif sql_dtypes[attr] in ["character", "character varying", "boolean", "uuid", "text"]: - data_type_lookup[attr] = "nominal" - data_type["nominal"].append(attr) - elif sql_dtypes[attr] in ["integer", "real", "smallint", "smallserial", "serial"]: - if self.cardinality[attr] < 13: - data_type_lookup[attr] = "nominal" - data_type["nominal"].append(attr) - else: - data_type_lookup[attr] = "quantitative" - data_type["quantitative"].append(attr) - elif "time" in sql_dtypes[attr] or "date" in sql_dtypes[attr]: - data_type_lookup[attr] = "temporal" - data_type["temporal"].append(attr) - self.data_type_lookup = data_type_lookup - self.data_type = data_type - def _append_rec(self,rec_infolist,recommendations:Dict): - if (recommendations["collection"] is not None and len(recommendations["collection"])>0): - 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 - 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}.") - show_prev = True - else: - rec_df = self - rec_df._message = Message() - # Add warning message if there exist ID fields - id_fields_str = "" - 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._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 - from lux.action.filter import filter - from lux.action.generalize import generalize - from lux.action.row_group import row_group - from lux.action.column_group import column_group - if (rec_df.pre_aggregated): - if (rec_df.columns.name is not None): - rec_df._append_rec(rec_infolist, row_group(rec_df)) - rec_df._append_rec(rec_infolist, column_group(rec_df)) - else: - if rec_df.recommendation == {}: - # 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") - 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) - - # 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) - lux.update_actions["flag"] = False - - # Store _rec_info into a more user-friendly dictionary form - rec_df.recommendation = {} - for rec_info in rec_infolist: - action_type = rec_info["action"] - vlist = rec_info["collection"] - if (rec_df._plot_config): - if (rec_df.current_vis): - for vis in rec_df.current_vis: vis._plot_config = rec_df.plot_config - for vis in vlist: vis._plot_config = rec_df.plot_config - if (len(vlist)>0): - rec_df.recommendation[action_type] = vlist - rec_df._rec_info = rec_infolist - self._widget = rec_df.render_widget() - elif (show_prev): # re-render widget for the current dataframe if previous rec is not recomputed - self._widget = rec_df.render_widget() - self._recs_fresh = True - - - ####################################################### - ############## LuxWidget Result Display ############### - ####################################################### - @property - def widget(self): - if(self._widget): - return self._widget - @property - def exported(self) -> Union[Dict[str,VisList], VisList]: - """ - Get selected visualizations as exported Vis List - - Notes - ----- - Convert the _selectedVisIdxs dictionary into a programmable VisList - Example _selectedVisIdxs : - {'Correlation': [0, 2], 'Occurrence': [1]} - indicating the 0th and 2nd vis from the `Correlation` tab is selected, and the 1st vis from the `Occurrence` tab is selected. - - Returns - ------- - Union[Dict[str,VisList], VisList] - When there are no exported vis, return empty list -> [] - When all the exported vis is from the same tab, return a VisList of selected visualizations. -> VisList(v1, v2...) - When the exported vis is from the different tabs, return a dictionary with the action name as key and selected visualizations in the VisList. -> {"Enhance": VisList(v1, v2...), "Filter": VisList(v5, v7...), ..} - """ - if not hasattr(self,"_widget"): - warnings.warn( - "\nNo widget attached to the dataframe." - "Please assign dataframe to an output variable.\n" - "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips" - , stacklevel=2) - return [] - exported_vis_lst = self._widget._selectedVisIdxs - 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" - ,stacklevel=2) - return [] - if len(exported_vis_lst) == 1 and "currentVis" in exported_vis_lst: - return self.current_vis - elif len(exported_vis_lst) > 1: - exported_vis = {} - if ("currentVis" in exported_vis_lst): - exported_vis["Current Vis"] = self.current_vis - for export_action in exported_vis_lst: - if (export_action != "currentVis"): - exported_vis[export_action] = VisList(list(map(self.recommendation[export_action].__getitem__, exported_vis_lst[export_action]))) - return exported_vis - 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( - "\nNo visualization selected to export.\n" - "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips" - ,stacklevel=2) - return [] - - def remove_deleted_recs(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 set_intent_on_click(self, change): - from IPython.display import display, clear_output - from lux.processor.Compiler import Compiler - - intent_action = list(self._widget.selectedIntentIndex.keys())[0] - vis = self.recommendation[intent_action][self._widget.selectedIntentIndex[intent_action][0]] - self.set_intent_as_vis(vis) - - self.maintain_metadata() - self.current_vis = Compiler.compile_intent(self, self._intent) - self.maintain_recs() - - with self.output: - clear_output() - display(self._widget) - - self._widget.observe(self.remove_deleted_recs, names='deletedIndices') - self._widget.observe(self.set_intent_on_click, names='selectedIntentIndex') - - def _repr_html_(self): - from IPython.display import display - from IPython.display import clear_output - import ipywidgets as widgets - - try: - if (self._pandas_only): - display(self.display_pandas()) - self._pandas_only=False - else: - if(self.index.nlevels>=2 or self.columns.nlevels >= 2): - warnings.warn( - "\nLux does not currently support dataframes " - "with hierarchical indexes.\n" - "Please convert the dataframe into a flat " - "table via `pandas.DataFrame.reset_index`.\n", - stacklevel=2, - ) - display(self.display_pandas()) - return - - if (len(self)<=0): - warnings.warn("\nLux can not operate on an empty dataframe.\nPlease check your input again.\n",stacklevel=2) - display(self.display_pandas()) - 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()) - return - self.maintain_metadata() - - if (self._intent!=[] and (not hasattr(self,"_compiled") or not self._compiled)): - from lux.processor.Compiler import Compiler - self.current_vis = Compiler.compile_intent(self, self._intent) - - if (lux.config.default_display == "lux"): - self._toggle_pandas_display = False - else: - self._toggle_pandas_display = True - - # 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.remove_deleted_recs, names='deletedIndices') - self._widget.observe(self.set_intent_on_click, names='selectedIntentIndex') - - 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')) - self.output = widgets.Output() - # box.children = [button,output] - # output.children = [button] - # display(box) - display(button, self.output) - def on_button_clicked(b): - with self.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: - 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=""): - """ - Generate a LuxWidget based on the LuxDataFrame - - Structure of widgetJSON: - { - 'current_vis': {}, - 'recommendation': [ - { - 'action': 'Correlation', - 'description': "some description", - 'vspec': [ - {Vega-Lite spec for vis 1}, - {Vega-Lite spec for vis 2}, - ... - ] - }, - ... repeat for other actions - ] - } - Parameters - ---------- - renderer : str, optional - Choice of visualization rendering library, by default "altair" - input_current_vis : lux.LuxDataFrame, optional - User-specified current vis to override default Current Vis, by default - """ - check_import_lux_widget() - import luxwidget - widgetJSON = self.to_JSON(self._rec_info, input_current_vis=input_current_vis) - return luxwidget.LuxWidget( - currentVis=widgetJSON["current_vis"], - recommendations=widgetJSON["recommendation"], - intent=LuxDataFrame.intent_to_string(self._intent), - message = self._message.to_html() - ) - @staticmethod - def intent_to_JSON(intent): - from lux.utils import utils - - filter_specs = utils.get_filter_specs(intent) - attrs_specs = utils.get_attrs_specs(intent) - - intent = {} - intent['attributes'] = [clause.attribute for clause in attrs_specs] - intent['filters'] = [clause.attribute for clause in filter_specs] - return intent - @staticmethod - def intent_to_string(intent): - if (intent): - return ", ".join([clause.to_string() for clause in intent]) - else: - return "" - - def to_JSON(self, rec_infolist, input_current_vis=""): - widget_spec = {} - if (self.current_vis): - self.executor.execute(self.current_vis, self) - widget_spec["current_vis"] = LuxDataFrame.current_vis_to_JSON(self.current_vis, input_current_vis) - else: - widget_spec["current_vis"] = {} - widget_spec["recommendation"] = [] - - # Recommended Collection - recCollection = LuxDataFrame.rec_to_JSON(rec_infolist) - widget_spec["recommendation"].extend(recCollection) - return widget_spec - - @staticmethod - def current_vis_to_JSON(vlist, input_current_vis=""): - current_vis_spec = {} - numVC = len(vlist) #number of visualizations in the vis list - if (numVC==1): - current_vis_spec = vlist[0].render_VSpec() - elif (numVC>1): - pass - return current_vis_spec - - @staticmethod - def rec_to_JSON(recs): - rec_lst = [] - import copy - rec_copy = copy.deepcopy(recs) - for idx,rec in enumerate(rec_copy): - if (len(rec["collection"])>0): - rec["vspec"] = [] - for vis in rec["collection"]: - chart = vis.render_VSpec() - rec["vspec"].append(chart) - rec_lst.append(rec) - # 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 - self._history.append_event("head", n=5) - return super(LuxDataFrame, self).head(n) - - def tail(self, n: int = 5): - self._prev = self - self._history.append_event("tail", n=5) - return super(LuxDataFrame, self).tail(n) - - def info(self, *args, **kwargs): - self._pandas_only=True - self._history.append_event("info",*args, **kwargs) - return super(LuxDataFrame, self).info(*args, **kwargs) - - def describe(self, *args, **kwargs): - self._pandas_only=True - self._history.append_event("describe",*args, **kwargs) - return super(LuxDataFrame, self).describe(*args, **kwargs) + """ + A subclass of pd.DataFrame that supports all dataframe operations while housing other variables and functions for generating visual recommendations. + """ + + # 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", + ] + + 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) + + self.executor_type = "Pandas" + self.executor = PandasExecutor() + self.SQLconnection = "" + self.table_name = "" + + self._sampled = None + self._default_pandas_display = True + self._toggle_pandas_display = True + self._plot_config = None + self._message = Message() + self._pandas_only = False + # Metadata + self.data_type_lookup = None + self.data_type = None + self.data_model_lookup = None + self.data_model = None + self.unique_values = None + self.cardinality = None + self._min_max = None + self.pre_aggregated = None + + @property + def _constructor(self): + return LuxDataFrame + + @property + def _constructor_sliced(self): + def f(*args, **kwargs): + s = LuxSeries(*args, **kwargs) + for attr in self._metadata: # propagate metadata + s.__dict__[attr] = getattr(self, attr, None) + return s + + return f + + @property + def history(self): + return self._history + + def maintain_metadata(self): + if ( + not hasattr(self, "_metadata_fresh") or not self._metadata_fresh + ): # Check that metadata has not yet been computed + if ( + len(self) > 0 + ): # only compute metadata information if the dataframe is non-empty + self.executor.compute_stats(self) + self.executor.compute_dataset_metadata(self) + self._infer_structure() + self._metadata_fresh = True + + def expire_recs(self): + self._recs_fresh = False + self.recommendation = {} + self.current_vis = None + self._widget = None + self._rec_info = None + self._sampled = None + + def expire_metadata(self): + # Set metadata as null + self._metadata_fresh = False + self.data_type_lookup = None + self.data_type = None + self.data_model_lookup = None + self.data_model = None + self.unique_values = None + self.cardinality = None + self._min_max = None + self.pre_aggregated = None + + ##################### + ## Override Pandas ## + ##################### + def __getattr__(self, name): + ret_value = super(LuxDataFrame, self).__getattr__(name) + self.expire_metadata() + self.expire_recs() + return ret_value + + def _set_axis(self, axis, labels): + super(LuxDataFrame, self)._set_axis(axis, labels) + self.expire_metadata() + self.expire_recs() + + def _update_inplace(self, *args, **kwargs): + super(LuxDataFrame, self)._update_inplace(*args, **kwargs) + self.expire_metadata() + self.expire_recs() + + def _set_item(self, key, value): + super(LuxDataFrame, self)._set_item(key, value) + self.expire_metadata() + self.expire_recs() + + 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 + not_int_index_flag = self.index.dtype != "int64" + small_df_flag = len(self) < 100 + self.pre_aggregated = ( + is_multi_index_flag or not_int_index_flag + ) and small_df_flag + if "Number of Records" in self.columns: + self.pre_aggregated = True + very_small_df_flag = len(self) <= 10 + if very_small_df_flag: + self.pre_aggregated = True + + def set_executor_type(self, exe): + if exe == "SQL": + import pkgutil + + if pkgutil.find_loader("psycopg2") is None: + raise ImportError( + "psycopg2 is not installed. Run `pip install psycopg2' to install psycopg2 to enable the Postgres connection." + ) + else: + import psycopg2 + from lux.executor.SQLExecutor import SQLExecutor + + self.executor = SQLExecutor + else: + from lux.executor.PandasExecutor import PandasExecutor + + self.executor = PandasExecutor() + self.executor_type = exe + + @property + def plot_config(self): + return self._plot_config + + @plot_config.setter + def plot_config(self, config_func: Callable): + """ + Modify plot aesthetic settings to all visualizations in the dataframe display + Currently only supported for Altair visualizations + Parameters + ---------- + config_func : Callable + A function that takes in an AltairChart (https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html) as input and returns an AltairChart as output + + Example + ---------- + Changing the color of marks and adding a title for all charts displayed for this dataframe + >>> df = pd.read_csv("lux/data/car.csv") + >>> def changeColorAddTitle(chart): + chart = chart.configure_mark(color="red") # change mark color to red + chart.title = "Custom Title" # add title to chart + return chart + >>> df.plot_config = changeColorAddTitle + >>> df + Change the opacity of all scatterplots displayed for this dataframe + >>> df = pd.read_csv("lux/data/olympic.csv") + >>> def changeOpacityScatterOnly(chart): + if chart.mark=='circle': + chart = chart.configure_mark(opacity=0.1) # lower opacity + return chart + >>> df.plot_config = changeOpacityScatterOnly + >>> df + """ + self._plot_config = config_func + self._recs_fresh = False + + def clear_plot_config(self): + self._plot_config = None + self._recs_fresh = False + + @property + def intent(self): + return self._intent + + @intent.setter + def intent(self, intent_input: Union[List[Union[str, Clause]], Vis]): + is_list_input = isinstance(intent_input, list) + is_vis_input = isinstance(intent_input, Vis) + if not (is_list_input or is_vis_input): + raise TypeError( + "Input intent must be either a list (of strings or lux.Clause) or a lux.Vis object." + "\nSee more at: https://lux-api.readthedocs.io/en/latest/source/guide/intent.html" + ) + if is_list_input: + self.set_intent(intent_input) + elif is_vis_input: + self.set_intent_as_vis(intent_input) + + def clear_intent(self): + self.intent = [] + + 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` + """ + self.expire_recs() + self._intent = intent + self._parse_validate_compile_intent() + + def _parse_validate_compile_intent(self): + from lux.processor.Parser import Parser + from lux.processor.Validator import Validator + + self._intent = Parser.parse(self._intent) + Validator.validate_intent(self._intent, self) + self.maintain_metadata() + from lux.processor.Compiler import Compiler + + self.current_vis = Compiler.compile_intent(self, self._intent) + + def copy_intent(self): + # creates a true copy of the dataframe's intent + output = [] + for clause in self._intent: + temp_clause = clause.copy_clause() + output.append(temp_clause) + return output + + def set_intent_as_vis(self, vis: Vis): + """ + Set intent of the dataframe as the Vis + + Parameters + ---------- + vis : Vis + """ + self.expire_recs() + self._intent = vis._inferred_intent + self._parse_validate_compile_intent() + + def to_pandas(self): + import lux.core + + return lux.core.originalDF(self, copy=False) + + @property + def recommendation(self): + return self._recommendation + + @recommendation.setter + def recommendation(self, recommendation: Dict): + self._recommendation = recommendation + + @property + def current_vis(self): + return self._current_vis + + @current_vis.setter + def current_vis(self, current_vis: Dict): + self._current_vis = current_vis + + def __repr__(self): + # TODO: _repr_ gets called from _repr_html, need to get rid of this call + return "" + + ####################################################### + ########## SQL Metadata, type, model schema ########### + ####################################################### + + def set_SQL_connection(self, connection, t_name): + self.SQLconnection = connection + self.table_name = t_name + self.compute_SQL_dataset_metadata() + self.set_executor_type("SQL") + + def compute_SQL_dataset_metadata(self): + self.get_SQL_attributes() + for attr in list(self.columns): + self[attr] = None + self.data_type_lookup = {} + self.data_type = {} + #####NOTE: since we aren't expecting users to do much data processing with the SQL database, should we just keep this + ##### in the initialization and do it just once + self.compute_SQL_data_type() + self.compute_SQL_stats() + self.data_model_lookup = {} + self.data_model = {} + self.compute_data_model() + + def compute_SQL_stats(self): + # precompute statistics + self.unique_values = {} + self._min_max = {} + + self.get_SQL_unique_values() + # self.get_SQL_cardinality() + for attribute in self.columns: + if self.data_type_lookup[attribute] == "quantitative": + self._min_max[attribute] = ( + self[attribute].min(), + self[attribute].max(), + ) + + def get_SQL_attributes(self): + if "." in self.table_name: + table_name = self.table_name[self.table_name.index(".") + 1 :] + else: + table_name = self.table_name + attr_query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '{}'".format( + table_name + ) + attributes = list(pd.read_sql(attr_query, self.SQLconnection)["column_name"]) + for attr in attributes: + self[attr] = None + + def get_SQL_cardinality(self): + cardinality = {} + for attr in list(self.columns): + card_query = pd.read_sql( + "SELECT Count(Distinct({})) FROM {}".format(attr, self.table_name), + self.SQLconnection, + ) + cardinality[attr] = list(card_query["count"])[0] + self.cardinality = cardinality + + def get_SQL_unique_values(self): + unique_vals = {} + for attr in list(self.columns): + unique_query = pd.read_sql( + "SELECT Distinct({}) FROM {}".format(attr, self.table_name), + self.SQLconnection, + ) + unique_vals[attr] = list(unique_query[attr]) + self.unique_values = unique_vals + + def compute_SQL_data_type(self): + data_type_lookup = {} + sql_dtypes = {} + self.get_SQL_cardinality() + if "." in self.table_name: + table_name = self.table_name[self.table_name.index(".") + 1 :] + else: + table_name = self.table_name + # get the data types of the attributes in the SQL table + for attr in list(self.columns): + datatype_query = "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{}' AND COLUMN_NAME = '{}'".format( + table_name, attr + ) + datatype = list( + pd.read_sql(datatype_query, self.SQLconnection)["data_type"] + )[0] + sql_dtypes[attr] = datatype + + data_type = {"quantitative": [], "nominal": [], "temporal": []} + for attr in list(self.columns): + if str(attr).lower() in ["month", "year"]: + data_type_lookup[attr] = "temporal" + data_type["temporal"].append(attr) + elif sql_dtypes[attr] in [ + "character", + "character varying", + "boolean", + "uuid", + "text", + ]: + data_type_lookup[attr] = "nominal" + data_type["nominal"].append(attr) + elif sql_dtypes[attr] in [ + "integer", + "real", + "smallint", + "smallserial", + "serial", + ]: + if self.cardinality[attr] < 13: + data_type_lookup[attr] = "nominal" + data_type["nominal"].append(attr) + else: + data_type_lookup[attr] = "quantitative" + data_type["quantitative"].append(attr) + elif "time" in sql_dtypes[attr] or "date" in sql_dtypes[attr]: + data_type_lookup[attr] = "temporal" + data_type["temporal"].append(attr) + self.data_type_lookup = data_type_lookup + self.data_type = data_type + + def _append_rec(self, rec_infolist, recommendations: Dict): + if ( + recommendations["collection"] is not None + and len(recommendations["collection"]) > 0 + ): + 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 + 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}." + ) + show_prev = True + else: + rec_df = self + rec_df._message = Message() + # Add warning message if there exist ID fields + id_fields_str = "" + 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._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 + from lux.action.filter import filter + from lux.action.generalize import generalize + from lux.action.row_group import row_group + from lux.action.column_group import column_group + + if rec_df.pre_aggregated: + if rec_df.columns.name is not None: + rec_df._append_rec(rec_infolist, row_group(rec_df)) + rec_df._append_rec(rec_infolist, column_group(rec_df)) + else: + if rec_df.recommendation == {}: + # 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") + 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) + + # 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) + lux.update_actions["flag"] = False + + # Store _rec_info into a more user-friendly dictionary form + rec_df.recommendation = {} + for rec_info in rec_infolist: + action_type = rec_info["action"] + vlist = rec_info["collection"] + if rec_df._plot_config: + if rec_df.current_vis: + for vis in rec_df.current_vis: + vis._plot_config = rec_df.plot_config + for vis in vlist: + vis._plot_config = rec_df.plot_config + if len(vlist) > 0: + rec_df.recommendation[action_type] = vlist + rec_df._rec_info = rec_infolist + self._widget = rec_df.render_widget() + elif ( + show_prev + ): # re-render widget for the current dataframe if previous rec is not recomputed + self._widget = rec_df.render_widget() + self._recs_fresh = True + + ####################################################### + ############## LuxWidget Result Display ############### + ####################################################### + @property + def widget(self): + if self._widget: + return self._widget + + @property + def exported(self) -> Union[Dict[str, VisList], VisList]: + """ + Get selected visualizations as exported Vis List + + Notes + ----- + Convert the _selectedVisIdxs dictionary into a programmable VisList + Example _selectedVisIdxs : + {'Correlation': [0, 2], 'Occurrence': [1]} + indicating the 0th and 2nd vis from the `Correlation` tab is selected, and the 1st vis from the `Occurrence` tab is selected. + + Returns + ------- + Union[Dict[str,VisList], VisList] + When there are no exported vis, return empty list -> [] + When all the exported vis is from the same tab, return a VisList of selected visualizations. -> VisList(v1, v2...) + When the exported vis is from the different tabs, return a dictionary with the action name as key and selected visualizations in the VisList. -> {"Enhance": VisList(v1, v2...), "Filter": VisList(v5, v7...), ..} + """ + if not hasattr(self, "_widget"): + warnings.warn( + "\nNo widget attached to the dataframe." + "Please assign dataframe to an output variable.\n" + "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips", + stacklevel=2, + ) + return [] + exported_vis_lst = self._widget._selectedVisIdxs + 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", + stacklevel=2, + ) + return [] + if len(exported_vis_lst) == 1 and "currentVis" in exported_vis_lst: + return self.current_vis + elif len(exported_vis_lst) > 1: + exported_vis = {} + if "currentVis" in exported_vis_lst: + exported_vis["Current Vis"] = self.current_vis + for export_action in exported_vis_lst: + if export_action != "currentVis": + exported_vis[export_action] = VisList( + list( + map( + self.recommendation[export_action].__getitem__, + exported_vis_lst[export_action], + ) + ) + ) + return exported_vis + 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( + "\nNo visualization selected to export.\n" + "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips", + stacklevel=2, + ) + return [] + + def remove_deleted_recs(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 set_intent_on_click(self, change): + from IPython.display import display, clear_output + from lux.processor.Compiler import Compiler + + intent_action = list(self._widget.selectedIntentIndex.keys())[0] + vis = self.recommendation[intent_action][ + self._widget.selectedIntentIndex[intent_action][0] + ] + self.set_intent_as_vis(vis) + + self.maintain_metadata() + self.current_vis = Compiler.compile_intent(self, self._intent) + self.maintain_recs() + + with self.output: + clear_output() + display(self._widget) + + self._widget.observe(self.remove_deleted_recs, names="deletedIndices") + self._widget.observe(self.set_intent_on_click, names="selectedIntentIndex") + + def _repr_html_(self): + from IPython.display import display + from IPython.display import clear_output + import ipywidgets as widgets + + try: + if self._pandas_only: + display(self.display_pandas()) + self._pandas_only = False + else: + if self.index.nlevels >= 2 or self.columns.nlevels >= 2: + warnings.warn( + "\nLux does not currently support dataframes " + "with hierarchical indexes.\n" + "Please convert the dataframe into a flat " + "table via `pandas.DataFrame.reset_index`.\n", + stacklevel=2, + ) + display(self.display_pandas()) + return + + if len(self) <= 0: + warnings.warn( + "\nLux can not operate on an empty dataframe.\nPlease check your input again.\n", + stacklevel=2, + ) + display(self.display_pandas()) + 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()) + return + self.maintain_metadata() + + if self._intent != [] and ( + not hasattr(self, "_compiled") or not self._compiled + ): + from lux.processor.Compiler import Compiler + + self.current_vis = Compiler.compile_intent(self, self._intent) + + if lux.config.default_display == "lux": + self._toggle_pandas_display = False + else: + self._toggle_pandas_display = True + + # 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.remove_deleted_recs, names="deletedIndices") + self._widget.observe( + self.set_intent_on_click, names="selectedIntentIndex" + ) + + 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"), + ) + self.output = widgets.Output() + # box.children = [button,output] + # output.children = [button] + # display(box) + display(button, self.output) + + def on_button_clicked(b): + with self.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: + 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=""): + """ + Generate a LuxWidget based on the LuxDataFrame + + Structure of widgetJSON: + { + 'current_vis': {}, + 'recommendation': [ + { + 'action': 'Correlation', + 'description': "some description", + 'vspec': [ + {Vega-Lite spec for vis 1}, + {Vega-Lite spec for vis 2}, + ... + ] + }, + ... repeat for other actions + ] + } + Parameters + ---------- + renderer : str, optional + Choice of visualization rendering library, by default "altair" + input_current_vis : lux.LuxDataFrame, optional + User-specified current vis to override default Current Vis, by default + """ + check_import_lux_widget() + import luxwidget + + widgetJSON = self.to_JSON(self._rec_info, input_current_vis=input_current_vis) + return luxwidget.LuxWidget( + currentVis=widgetJSON["current_vis"], + recommendations=widgetJSON["recommendation"], + intent=LuxDataFrame.intent_to_string(self._intent), + message=self._message.to_html(), + ) + + @staticmethod + def intent_to_JSON(intent): + from lux.utils import utils + + filter_specs = utils.get_filter_specs(intent) + attrs_specs = utils.get_attrs_specs(intent) + + intent = {} + intent["attributes"] = [clause.attribute for clause in attrs_specs] + intent["filters"] = [clause.attribute for clause in filter_specs] + return intent + + @staticmethod + def intent_to_string(intent): + if intent: + return ", ".join([clause.to_string() for clause in intent]) + else: + return "" + + def to_JSON(self, rec_infolist, input_current_vis=""): + widget_spec = {} + if self.current_vis: + self.executor.execute(self.current_vis, self) + widget_spec["current_vis"] = LuxDataFrame.current_vis_to_JSON( + self.current_vis, input_current_vis + ) + else: + widget_spec["current_vis"] = {} + widget_spec["recommendation"] = [] + + # Recommended Collection + recCollection = LuxDataFrame.rec_to_JSON(rec_infolist) + widget_spec["recommendation"].extend(recCollection) + return widget_spec + + @staticmethod + def current_vis_to_JSON(vlist, input_current_vis=""): + current_vis_spec = {} + numVC = len(vlist) # number of visualizations in the vis list + if numVC == 1: + current_vis_spec = vlist[0].render_VSpec() + elif numVC > 1: + pass + return current_vis_spec + + @staticmethod + def rec_to_JSON(recs): + rec_lst = [] + import copy + + rec_copy = copy.deepcopy(recs) + for idx, rec in enumerate(rec_copy): + if len(rec["collection"]) > 0: + rec["vspec"] = [] + for vis in rec["collection"]: + chart = vis.render_VSpec() + rec["vspec"].append(chart) + rec_lst.append(rec) + # 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 + self._history.append_event("head", n=5) + return super(LuxDataFrame, self).head(n) + + def tail(self, n: int = 5): + self._prev = self + self._history.append_event("tail", n=5) + return super(LuxDataFrame, self).tail(n) + + def info(self, *args, **kwargs): + self._pandas_only = True + self._history.append_event("info", *args, **kwargs) + return super(LuxDataFrame, self).info(*args, **kwargs) + + def describe(self, *args, **kwargs): + self._pandas_only = True + self._history.append_event("describe", *args, **kwargs) + return super(LuxDataFrame, self).describe(*args, **kwargs) diff --git a/lux/core/series.py b/lux/core/series.py index ecfd9aa2..d987b4e5 100644 --- a/lux/core/series.py +++ b/lux/core/series.py @@ -1,5 +1,5 @@ # 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 @@ -13,24 +13,45 @@ # limitations under the License. import pandas as pd + + class LuxSeries(pd.Series): - _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'] - def __init__(self,*args, **kw): - super(LuxSeries, self).__init__(*args, **kw) - - @property - def _constructor(self): - return LuxSeries - - @property - def _constructor_expanddim(self): - from lux.core.frame import LuxDataFrame - def f(*args, **kwargs): - df = LuxDataFrame(*args, **kwargs) - for attr in self._metadata: - df.__dict__[attr] = getattr(self, attr, None) - return df - f._get_axis_number = super(LuxSeries, self)._get_axis_number - return f \ No newline at end of file + _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", + ] + + def __init__(self, *args, **kw): + super(LuxSeries, self).__init__(*args, **kw) + + @property + def _constructor(self): + return LuxSeries + + @property + def _constructor_expanddim(self): + from lux.core.frame import LuxDataFrame + + def f(*args, **kwargs): + df = LuxDataFrame(*args, **kwargs) + for attr in self._metadata: + df.__dict__[attr] = getattr(self, attr, None) + return df + + f._get_axis_number = super(LuxSeries, self)._get_axis_number + return f diff --git a/lux/executor/Executor.py b/lux/executor/Executor.py index f2216131..972f6fb6 100644 --- a/lux/executor/Executor.py +++ b/lux/executor/Executor.py @@ -1,5 +1,5 @@ # 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 @@ -14,26 +14,31 @@ from lux.vis.VisList import VisList from lux.utils import utils + + class Executor: """ Abstract class for the execution engine that fetches data for a given vis on a LuxDataFrame - """ + """ + def __init__(self): self.name = "Executor" def __repr__(self): return f"" + @staticmethod - def execute(vis_collection:VisList, ldf): + def execute(vis_collection: VisList, ldf): return NotImplemented @staticmethod def execute_aggregate(vis, ldf): return NotImplemented + @staticmethod def execute_binning(vis, ldf): return NotImplemented - + @staticmethod def execute_filter(vis, ldf): return NotImplemented @@ -61,4 +66,4 @@ def reverseMapping(self, map): for valKey in map: for val in map[valKey]: reverse_map[val] = valKey - return reverse_map \ No newline at end of file + return reverse_map diff --git a/lux/executor/PandasExecutor.py b/lux/executor/PandasExecutor.py index a8d17d36..64fa2e54 100644 --- a/lux/executor/PandasExecutor.py +++ b/lux/executor/PandasExecutor.py @@ -1,5 +1,5 @@ # 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 @@ -25,33 +25,42 @@ class PandasExecutor(Executor): - ''' + """ Given a Vis objects with complete specifications, fetch and process data using Pandas dataframe operations. - ''' + """ + def __init__(self): self.name = "PandasExecutor" def __repr__(self): return f"" + @staticmethod - def execute_sampling(ldf:LuxDataFrame): + def execute_sampling(ldf: LuxDataFrame): # General Sampling for entire dataframe SAMPLE_START = 10000 SAMPLE_CAP = 30000 SAMPLE_FRAC = 0.75 if len(ldf) > SAMPLE_CAP: - if (ldf._sampled is None): # memoize unfiltered sample df - ldf._sampled = ldf.sample(n = SAMPLE_CAP , random_state = 1) - ldf._message.add_unique(f"Large dataframe detected: Lux is only visualizing a random sample capped at {SAMPLE_CAP} rows.", priority=99) + if ldf._sampled is None: # memoize unfiltered sample df + ldf._sampled = ldf.sample(n=SAMPLE_CAP, random_state=1) + ldf._message.add_unique( + f"Large dataframe detected: Lux is only visualizing a random sample capped at {SAMPLE_CAP} rows.", + priority=99, + ) elif len(ldf) > SAMPLE_START: - if (ldf._sampled is None): # memoize unfiltered sample df - ldf._sampled = ldf.sample(frac= SAMPLE_FRAC, random_state = 1) - ldf._message.add_unique(f"Large dataframe detected: Lux is only visualizing a random sample of {len(ldf._sampled)} rows.", priority=99) + if ldf._sampled is None: # memoize unfiltered sample df + ldf._sampled = ldf.sample(frac=SAMPLE_FRAC, random_state=1) + ldf._message.add_unique( + f"Large dataframe detected: Lux is only visualizing a random sample of {len(ldf._sampled)} rows.", + priority=99, + ) else: ldf._sampled = ldf + @staticmethod - def execute(vislist:VisList, ldf:LuxDataFrame): - ''' + def execute(vislist: VisList, ldf: LuxDataFrame): + """ Given a VisList, fetch the data required to render the vis. 1) Apply filters 2) Retrieve relevant attribute @@ -68,36 +77,39 @@ def execute(vislist:VisList, ldf:LuxDataFrame): Returns ------- None - ''' + """ PandasExecutor.execute_sampling(ldf) for vis in vislist: - vis._vis_data = ldf._sampled # The vis data starts off being original or sampled dataframe + vis._vis_data = ( + ldf._sampled + ) # The vis data starts off being original or sampled dataframe filter_executed = PandasExecutor.execute_filter(vis) # Select relevant data based on attribute information attributes = set([]) for clause in vis._inferred_intent: - if (clause.attribute): - if (clause.attribute!="Record"): + if clause.attribute: + if clause.attribute != "Record": attributes.add(clause.attribute) # TODO: Add some type of cap size on Nrows ? vis._vis_data = vis.data[list(attributes)] - if (vis.mark =="bar" or vis.mark =="line"): - PandasExecutor.execute_aggregate(vis,isFiltered = filter_executed) - elif (vis.mark =="histogram"): + if vis.mark == "bar" or vis.mark == "line": + PandasExecutor.execute_aggregate(vis, isFiltered=filter_executed) + elif vis.mark == "histogram": PandasExecutor.execute_binning(vis) - elif (vis.mark =="scatter"): + elif vis.mark == "scatter": HBIN_START = 5000 - if (len(ldf)>HBIN_START): + if len(ldf) > HBIN_START: vis._postbin = True - ldf._message.add_unique(f"Large scatterplots detected: Lux is automatically binning scatterplots to heatmaps.", priority=98) + ldf._message.add_unique( + f"Large scatterplots detected: Lux is automatically binning scatterplots to heatmaps.", + priority=98, + ) # vis._mark = "heatmap" - # PandasExecutor.execute_2D_binning(vis) # Lazy Evaluation (Early pruning based on interestingness) - - + # PandasExecutor.execute_2D_binning(vis) # Lazy Evaluation (Early pruning based on interestingness) @staticmethod - def execute_aggregate(vis: Vis,isFiltered = True): - ''' + def execute_aggregate(vis: Vis, isFiltered=True): + """ Aggregate data points on an axis for bar or line charts Parameters @@ -110,92 +122,128 @@ def execute_aggregate(vis: Vis,isFiltered = True): Returns ------- None - ''' + """ import numpy as np x_attr = vis.get_attr_by_channel("x")[0] y_attr = vis.get_attr_by_channel("y")[0] has_color = False - groupby_attr ="" - measure_attr ="" - if (x_attr.aggregation is None or y_attr.aggregation is None): + groupby_attr = "" + measure_attr = "" + if x_attr.aggregation is None or y_attr.aggregation is None: return - if (y_attr.aggregation!=""): + if y_attr.aggregation != "": groupby_attr = x_attr measure_attr = y_attr agg_func = y_attr.aggregation - if (x_attr.aggregation!=""): + if x_attr.aggregation != "": groupby_attr = y_attr measure_attr = x_attr agg_func = x_attr.aggregation - if (groupby_attr.attribute in vis.data.unique_values.keys()): + if groupby_attr.attribute in vis.data.unique_values.keys(): attr_unique_vals = vis.data.unique_values[groupby_attr.attribute] - #checks if color is specified in the Vis + # checks if color is specified in the Vis if len(vis.get_attr_by_channel("color")) == 1: color_attr = vis.get_attr_by_channel("color")[0] color_attr_vals = vis.data.unique_values[color_attr.attribute] color_cardinality = len(color_attr_vals) - #NOTE: might want to have a check somewhere to not use categorical variables with greater than some number of categories as a Color variable---------------- + # NOTE: might want to have a check somewhere to not use categorical variables with greater than some number of categories as a Color variable---------------- has_color = True else: color_cardinality = 1 - - if (measure_attr!=""): - if (measure_attr.attribute=="Record"): + + if measure_attr != "": + if measure_attr.attribute == "Record": vis._vis_data = vis.data.reset_index() - #if color is specified, need to group by groupby_attr and color_attr + # if color is specified, need to group by groupby_attr and color_attr if has_color: - vis._vis_data = vis.data.groupby([groupby_attr.attribute, color_attr.attribute]).count().reset_index() - vis._vis_data = vis.data.rename(columns={"index":"Record"}) - vis._vis_data = vis.data[[groupby_attr.attribute,color_attr.attribute,"Record"]] + vis._vis_data = ( + vis.data.groupby([groupby_attr.attribute, color_attr.attribute]) + .count() + .reset_index() + ) + vis._vis_data = vis.data.rename(columns={"index": "Record"}) + vis._vis_data = vis.data[ + [groupby_attr.attribute, color_attr.attribute, "Record"] + ] else: - vis._vis_data = vis.data.groupby(groupby_attr.attribute).count().reset_index() - vis._vis_data = vis.data.rename(columns={"index":"Record"}) - vis._vis_data = vis.data[[groupby_attr.attribute,"Record"]] + vis._vis_data = ( + vis.data.groupby(groupby_attr.attribute).count().reset_index() + ) + vis._vis_data = vis.data.rename(columns={"index": "Record"}) + vis._vis_data = vis.data[[groupby_attr.attribute, "Record"]] else: - #if color is specified, need to group by groupby_attr and color_attr + # if color is specified, need to group by groupby_attr and color_attr if has_color: - groupby_result = vis.data.groupby([groupby_attr.attribute, color_attr.attribute]) + groupby_result = vis.data.groupby( + [groupby_attr.attribute, color_attr.attribute] + ) else: groupby_result = vis.data.groupby(groupby_attr.attribute) groupby_result = groupby_result.agg(agg_func) intermediate = groupby_result.reset_index() vis._vis_data = intermediate.__finalize__(vis.data) result_vals = list(vis.data[groupby_attr.attribute]) - #create existing group by attribute combinations if color is specified - #this is needed to check what combinations of group_by_attr and color_attr values have a non-zero number of elements in them + # create existing group by attribute combinations if color is specified + # this is needed to check what combinations of group_by_attr and color_attr values have a non-zero number of elements in them if has_color: res_color_combi_vals = [] result_color_vals = list(vis.data[color_attr.attribute]) for i in range(0, len(result_vals)): res_color_combi_vals.append([result_vals[i], result_color_vals[i]]) # For filtered aggregation that have missing groupby-attribute values, set these aggregated value as 0, since no datapoints - if (isFiltered or has_color and attr_unique_vals): + if isFiltered or has_color and attr_unique_vals: N_unique_vals = len(attr_unique_vals) - if (len(result_vals) != N_unique_vals*color_cardinality): + if len(result_vals) != N_unique_vals * color_cardinality: columns = vis.data.columns if has_color: - df = pd.DataFrame({columns[0]: attr_unique_vals*color_cardinality, columns[1]: pd.Series(color_attr_vals).repeat(N_unique_vals)}) - vis._vis_data = vis.data.merge(df, on=[columns[0],columns[1]], how='right', suffixes=['', '_right']) + df = pd.DataFrame( + { + columns[0]: attr_unique_vals * color_cardinality, + columns[1]: pd.Series(color_attr_vals).repeat( + N_unique_vals + ), + } + ) + vis._vis_data = vis.data.merge( + df, + on=[columns[0], columns[1]], + how="right", + suffixes=["", "_right"], + ) for col in columns[2:]: - vis.data[col] = vis.data[col].fillna(0) #Triggers __setitem__ - assert len(list(vis.data[groupby_attr.attribute])) == N_unique_vals*len(color_attr_vals), f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute, color_attr.attribute}`." - vis._vis_data = vis.data.iloc[:,:3] # Keep only the three relevant columns not the *_right columns resulting from merge + vis.data[col] = vis.data[col].fillna( + 0 + ) # Triggers __setitem__ + assert len( + list(vis.data[groupby_attr.attribute]) + ) == N_unique_vals * len( + color_attr_vals + ), f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute, color_attr.attribute}`." + vis._vis_data = vis.data.iloc[ + :, :3 + ] # Keep only the three relevant columns not the *_right columns resulting from merge else: df = pd.DataFrame({columns[0]: attr_unique_vals}) - - vis._vis_data = vis.data.merge(df, on=columns[0], how='right', suffixes=['', '_right']) + + vis._vis_data = vis.data.merge( + df, on=columns[0], how="right", suffixes=["", "_right"] + ) for col in columns[1:]: vis.data[col] = vis.data[col].fillna(0) - assert len(list(vis.data[groupby_attr.attribute])) == N_unique_vals, f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute}`." - vis._vis_data = vis.data.sort_values(by=groupby_attr.attribute, ascending=True) + assert ( + len(list(vis.data[groupby_attr.attribute])) == N_unique_vals + ), f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute}`." + vis._vis_data = vis.data.sort_values( + by=groupby_attr.attribute, ascending=True + ) vis._vis_data = vis.data.reset_index() vis._vis_data = vis.data.drop(columns="index") @staticmethod def execute_binning(vis: Vis): - ''' + """ Binning of data points for generating histograms Parameters @@ -208,35 +256,48 @@ def execute_binning(vis: Vis): Returns ------- None - ''' + """ import numpy as np - bin_attribute = list(filter(lambda x: x.bin_size!=0,vis._inferred_intent))[0] + + bin_attribute = list(filter(lambda x: x.bin_size != 0, vis._inferred_intent))[0] if not np.isnan(vis.data[bin_attribute.attribute]).all(): - series = vis.data[bin_attribute.attribute].dropna() # np.histogram breaks if array contain NaN - #TODO:binning runs for name attribte. Name attribute has datatype quantitative which is wrong. - counts,bin_edges = np.histogram(series,bins=bin_attribute.bin_size) - #bin_edges of size N+1, so need to compute bin_center as the bin location - bin_center = np.mean(np.vstack([bin_edges[0:-1],bin_edges[1:]]), axis=0) + series = vis.data[ + bin_attribute.attribute + ].dropna() # np.histogram breaks if array contain NaN + # TODO:binning runs for name attribte. Name attribute has datatype quantitative which is wrong. + counts, bin_edges = np.histogram(series, bins=bin_attribute.bin_size) + # bin_edges of size N+1, so need to compute bin_center as the bin location + bin_center = np.mean(np.vstack([bin_edges[0:-1], bin_edges[1:]]), axis=0) # TODO: Should vis.data be a LuxDataFrame or a Pandas DataFrame? - vis._vis_data = pd.DataFrame(np.array([bin_center,counts]).T,columns=[bin_attribute.attribute, "Number of Records"]) + vis._vis_data = pd.DataFrame( + np.array([bin_center, counts]).T, + columns=[bin_attribute.attribute, "Number of Records"], + ) @staticmethod def execute_filter(vis: Vis): - assert vis.data is not None, "execute_filter assumes input vis.data is populated (if not, populate with LuxDataFrame values)" + assert ( + vis.data is not None + ), "execute_filter assumes input vis.data is populated (if not, populate with LuxDataFrame values)" filters = utils.get_filter_specs(vis._inferred_intent) - - if (filters): + + if filters: # TODO: Need to handle OR logic for filter in filters: - vis._vis_data = PandasExecutor.apply_filter(vis.data, filter.attribute, filter.filter_op, filter.value) + vis._vis_data = PandasExecutor.apply_filter( + vis.data, filter.attribute, filter.filter_op, filter.value + ) return True else: return False + @staticmethod - def apply_filter(df: pd.DataFrame, attribute:str, op: str, val: object) -> pd.DataFrame: + def apply_filter( + df: pd.DataFrame, attribute: str, op: str, val: object + ) -> pd.DataFrame: """ Helper function for applying filter to a dataframe - + Parameters ---------- df : pandas.DataFrame @@ -246,69 +307,84 @@ def apply_filter(df: pd.DataFrame, attribute:str, op: str, val: object) -> pd.Da op : str Filter operation, '=', '<', '>', '<=', '>=', '!=' val : object - Filter value - + Filter value + Returns ------- df: pandas.DataFrame Dataframe resulting from the filter operation - """ - if (op == '='): + """ + if op == "=": return df[df[attribute] == val] - elif (op == '<'): + elif op == "<": return df[df[attribute] < val] - elif (op == '>'): + elif op == ">": return df[df[attribute] > val] - elif (op == '<='): + elif op == "<=": return df[df[attribute] <= val] - elif (op == '>='): + elif op == ">=": return df[df[attribute] >= val] - elif (op == '!='): + elif op == "!=": return df[df[attribute] != val] return df + @staticmethod def execute_2D_binning(vis: Vis): - pd.reset_option('mode.chained_assignment') - with pd.option_context('mode.chained_assignment', None): + pd.reset_option("mode.chained_assignment") + with pd.option_context("mode.chained_assignment", None): x_attr = vis.get_attr_by_channel("x")[0] y_attr = vis.get_attr_by_channel("y")[0] - - vis._vis_data.loc[:,"xBin"] = pd.cut(vis._vis_data[x_attr.attribute], bins=40) - vis._vis_data.loc[:,"yBin"] = pd.cut(vis._vis_data[y_attr.attribute], bins=40) + + vis._vis_data.loc[:, "xBin"] = pd.cut( + vis._vis_data[x_attr.attribute], bins=40 + ) + vis._vis_data.loc[:, "yBin"] = pd.cut( + vis._vis_data[y_attr.attribute], bins=40 + ) color_attr = vis.get_attr_by_channel("color") - if (len(color_attr)>0): + if len(color_attr) > 0: color_attr = color_attr[0] - groups = vis._vis_data.groupby(['xBin','yBin'])[color_attr.attribute] - if (color_attr.data_type == "nominal"): + groups = vis._vis_data.groupby(["xBin", "yBin"])[color_attr.attribute] + if color_attr.data_type == "nominal": # Compute mode and count. Mode aggregates each cell by taking the majority vote for the category variable. In cases where there is ties across categories, pick the first item (.iat[0]) - result = groups.agg([("count","count"), - (color_attr.attribute,lambda x: pd.Series.mode(x).iat[0]) - ]).reset_index() - elif (color_attr.data_type == "quantitative"): + result = groups.agg( + [ + ("count", "count"), + (color_attr.attribute, lambda x: pd.Series.mode(x).iat[0]), + ] + ).reset_index() + elif color_attr.data_type == "quantitative": # Compute the average of all values in the bin - result = groups.agg([("count","count"), - (color_attr.attribute,"mean") - ]).reset_index() + result = groups.agg( + [("count", "count"), (color_attr.attribute, "mean")] + ).reset_index() result = result.dropna() else: - groups = vis._vis_data.groupby(['xBin','yBin'])[x_attr.attribute] - result = groups.agg("count").reset_index(name=x_attr.attribute) # .agg in this line throws SettingWithCopyWarning - result = result.rename(columns={x_attr.attribute:"count"}) - result = result[result["count"]!=0] + groups = vis._vis_data.groupby(["xBin", "yBin"])[x_attr.attribute] + result = groups.agg("count").reset_index( + name=x_attr.attribute + ) # .agg in this line throws SettingWithCopyWarning + result = result.rename(columns={x_attr.attribute: "count"}) + result = result[result["count"] != 0] # convert type to facilitate weighted correlation interestingess calculation - result.loc[:,"xBinStart"] = result["xBin"].apply(lambda x: x.left).astype('float') - result.loc[:,"xBinEnd"] = result["xBin"].apply(lambda x: x.right) + result.loc[:, "xBinStart"] = ( + result["xBin"].apply(lambda x: x.left).astype("float") + ) + result.loc[:, "xBinEnd"] = result["xBin"].apply(lambda x: x.right) - result.loc[:,"yBinStart"] = result["yBin"].apply(lambda x: x.left).astype('float') - result.loc[:,"yBinEnd"] = result["yBin"].apply(lambda x: x.right) + result.loc[:, "yBinStart"] = ( + result["yBin"].apply(lambda x: x.left).astype("float") + ) + result.loc[:, "yBinEnd"] = result["yBin"].apply(lambda x: x.right) + + vis._vis_data = result.drop(columns=["xBin", "yBin"]) - vis._vis_data = result.drop(columns=["xBin","yBin"]) ####################################################### ############ Metadata: data type, model ############# ####################################################### - def compute_dataset_metadata(self, ldf:LuxDataFrame): + def compute_dataset_metadata(self, ldf: LuxDataFrame): ldf.data_type_lookup = {} ldf.data_type = {} self.compute_data_type(ldf) @@ -316,10 +392,10 @@ def compute_dataset_metadata(self, ldf:LuxDataFrame): ldf.data_model = {} self.compute_data_model(ldf) - def compute_data_type(self, ldf:LuxDataFrame): + def compute_data_type(self, ldf: LuxDataFrame): for attr in list(ldf.columns): - temporal_var_list = ["month", "year","day","date","time"] - if (isinstance(attr,pd._libs.tslibs.timestamps.Timestamp)): + temporal_var_list = ["month", "year", "day", "date", "time"] + if isinstance(attr, pd._libs.tslibs.timestamps.Timestamp): # If timestamp, make the dictionary keys the _repr_ (e.g., TimeStamp('2020-04-05 00.000')--> '2020-04-05') ldf.data_type_lookup[attr] = "temporal" # elif any(var in str(attr).lower() for var in temporal_var_list): @@ -327,71 +403,81 @@ def compute_data_type(self, ldf:LuxDataFrame): ldf.data_type_lookup[attr] = "temporal" elif pd.api.types.is_float_dtype(ldf.dtypes[attr]): ldf.data_type_lookup[attr] = "quantitative" - elif pd.api.types.is_integer_dtype(ldf.dtypes[attr]): + elif pd.api.types.is_integer_dtype(ldf.dtypes[attr]): # See if integer value is quantitative or nominal by checking if the ratio of cardinality/data size is less than 0.4 and if there are less than 10 unique values - if (ldf.pre_aggregated): - if (ldf.cardinality[attr]==len(ldf)): + if ldf.pre_aggregated: + if ldf.cardinality[attr] == len(ldf): ldf.data_type_lookup[attr] = "nominal" - if ldf.cardinality[attr]/len(ldf) < 0.4 and ldf.cardinality[attr]<20: + if ( + ldf.cardinality[attr] / len(ldf) < 0.4 + and ldf.cardinality[attr] < 20 + ): ldf.data_type_lookup[attr] = "nominal" else: ldf.data_type_lookup[attr] = "quantitative" - if check_if_id_like(ldf,attr): + if check_if_id_like(ldf, attr): ldf.data_type_lookup[attr] = "id" # Eliminate this clause because a single NaN value can cause the dtype to be object elif pd.api.types.is_string_dtype(ldf.dtypes[attr]): - if check_if_id_like(ldf,attr): + if check_if_id_like(ldf, attr): ldf.data_type_lookup[attr] = "id" else: ldf.data_type_lookup[attr] = "nominal" - elif is_datetime_series(ldf.dtypes[attr]): #check if attribute is any type of datetime dtype + elif is_datetime_series( + ldf.dtypes[attr] + ): # check if attribute is any type of datetime dtype ldf.data_type_lookup[attr] = "temporal" else: - ldf.data_type_lookup[attr] = "nominal" + ldf.data_type_lookup[attr] = "nominal" # for attr in list(df.dtypes[df.dtypes=="int64"].keys()): # if self.cardinality[attr]>50: - if (ldf.index.dtype !='int64' and ldf.index.name): + if ldf.index.dtype != "int64" and ldf.index.name: ldf.data_type_lookup[ldf.index.name] = "nominal" ldf.data_type = self.mapping(ldf.data_type_lookup) from pandas.api.types import is_datetime64_any_dtype as is_datetime + non_datetime_attrs = [] for attr in ldf.columns: - if ldf.data_type_lookup[attr] == 'temporal' and not is_datetime(ldf[attr]): + if ldf.data_type_lookup[attr] == "temporal" and not is_datetime(ldf[attr]): non_datetime_attrs.append(attr) if len(non_datetime_attrs) == 1: warnings.warn( - f"\nLux detects that the attribute '{non_datetime_attrs[0]}' may be temporal.\n" - "In order to display visualizations for this attribute accurately, temporal attributes should be converted to Pandas Datetime objects.\n\n" - "Please consider converting this attribute using the pd.to_datetime function and providing a 'format' parameter to specify datetime format of the attribute.\n" - "For example, you can convert the 'month' attribute in a dataset to Datetime type via the following command:\n\n\t df['month'] = pd.to_datetime(df['month'], format='%m')\n\n" - "See more at: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html\n" - ,stacklevel=2) + f"\nLux detects that the attribute '{non_datetime_attrs[0]}' may be temporal.\n" + "In order to display visualizations for this attribute accurately, temporal attributes should be converted to Pandas Datetime objects.\n\n" + "Please consider converting this attribute using the pd.to_datetime function and providing a 'format' parameter to specify datetime format of the attribute.\n" + "For example, you can convert the 'month' attribute in a dataset to Datetime type via the following command:\n\n\t df['month'] = pd.to_datetime(df['month'], format='%m')\n\n" + "See more at: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html\n", + stacklevel=2, + ) elif len(non_datetime_attrs) > 1: warnings.warn( - f"\nLux detects that attributes {non_datetime_attrs} may be temporal.\n" - "In order to display visualizations for these attributes accurately, temporal attributes should be converted to Pandas Datetime objects.\n\n" - "Please consider converting these attributes using the pd.to_datetime function and providing a 'format' parameter to specify datetime format of the attribute.\n" - "For example, you can convert the 'month' attribute in a dataset to Datetime type via the following command:\n\n\t df['month'] = pd.to_datetime(df['month'], format='%m')\n\n" - "See more at: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html\n" - ,stacklevel=2) - def compute_data_model(self, ldf:LuxDataFrame): + f"\nLux detects that attributes {non_datetime_attrs} may be temporal.\n" + "In order to display visualizations for these attributes accurately, temporal attributes should be converted to Pandas Datetime objects.\n\n" + "Please consider converting these attributes using the pd.to_datetime function and providing a 'format' parameter to specify datetime format of the attribute.\n" + "For example, you can convert the 'month' attribute in a dataset to Datetime type via the following command:\n\n\t df['month'] = pd.to_datetime(df['month'], format='%m')\n\n" + "See more at: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html\n", + stacklevel=2, + ) + + def compute_data_model(self, ldf: LuxDataFrame): ldf.data_model = { "measure": ldf.data_type["quantitative"], - "dimension": ldf.data_type["nominal"] + ldf.data_type["temporal"] + ldf.data_type["id"] + "dimension": ldf.data_type["nominal"] + + ldf.data_type["temporal"] + + ldf.data_type["id"], } ldf.data_model_lookup = self.reverseMapping(ldf.data_model) - - def compute_stats(self, ldf:LuxDataFrame): + def compute_stats(self, ldf: LuxDataFrame): # precompute statistics ldf.unique_values = {} ldf._min_max = {} ldf.cardinality = {} for attribute in ldf.columns: - - if (isinstance(attribute,pd._libs.tslibs.timestamps.Timestamp)): + + if isinstance(attribute, pd._libs.tslibs.timestamps.Timestamp): # If timestamp, make the dictionary keys the _repr_ (e.g., TimeStamp('2020-04-05 00.000')--> '2020-04-05') attribute_repr = str(attribute._date_repr) else: @@ -399,19 +485,22 @@ def compute_stats(self, ldf:LuxDataFrame): ldf.unique_values[attribute_repr] = list(ldf[attribute_repr].unique()) ldf.cardinality[attribute_repr] = len(ldf.unique_values[attribute_repr]) - + # commenting this optimization out to make sure I can filter by cardinality when showing recommended vis # if ldf.dtypes[attribute] != "float64":# and not pd.api.types.is_datetime64_ns_dtype(self.dtypes[attribute]): # ldf.unique_values[attribute_repr] = list(ldf[attribute].unique()) # ldf.cardinality[attribute_repr] = len(ldf.unique_values[attribute]) - # else: + # else: # ldf.cardinality[attribute_repr] = 999 # special value for non-numeric attribute - + if ldf.dtypes[attribute] == "float64" or ldf.dtypes[attribute] == "int64": - ldf._min_max[attribute_repr] = (ldf[attribute].min(), ldf[attribute].max()) + ldf._min_max[attribute_repr] = ( + ldf[attribute].min(), + ldf[attribute].max(), + ) - if (ldf.index.dtype !='int64'): + if ldf.index.dtype != "int64": index_column_name = ldf.index.name ldf.unique_values[index_column_name] = list(ldf.index) - ldf.cardinality[index_column_name] = len(ldf.index) \ No newline at end of file + ldf.cardinality[index_column_name] = len(ldf.index) diff --git a/lux/executor/SQLExecutor.py b/lux/executor/SQLExecutor.py index 65500d55..2cca392d 100644 --- a/lux/executor/SQLExecutor.py +++ b/lux/executor/SQLExecutor.py @@ -1,5 +1,5 @@ # 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 @@ -20,10 +20,12 @@ from lux.utils import utils import math + class SQLExecutor(Executor): """ Given a Vis objects with complete specifications, fetch and process data using SQL operations. """ + def __init__(self): self.name = "Executor" self.selection = [] @@ -34,150 +36,225 @@ def __repr__(self): return f"" @staticmethod - def execute(vislist:VisList, ldf: LuxDataFrame): + def execute(vislist: VisList, ldf: LuxDataFrame): import pandas as pd - ''' + + """ Given a VisList, fetch the data required to render the vis 1) Apply filters 2) Retreive relevant attribute 3) return a DataFrame with relevant results - ''' + """ for vis in vislist: # Select relevant data based on attribute information attributes = set([]) for clause in vis._inferred_intent: - if (clause.attribute): - if (clause.attribute=="Record"): + if clause.attribute: + if clause.attribute == "Record": attributes.add(clause.attribute) - #else: + # else: attributes.add(clause.attribute) if vis.mark not in ["bar", "line", "histogram"]: where_clause, filterVars = SQLExecutor.execute_filter(vis) required_variables = attributes | set(filterVars) required_variables = ",".join(required_variables) - row_count = list(pd.read_sql("SELECT COUNT(*) FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection)['count'])[0] + row_count = list( + pd.read_sql( + "SELECT COUNT(*) FROM {} {}".format( + ldf.table_name, where_clause + ), + ldf.SQLconnection, + )["count"] + )[0] if row_count > 10000: - query = "SELECT {} FROM {} {} ORDER BY random() LIMIT 10000".format(required_variables, ldf.table_name, where_clause) + query = "SELECT {} FROM {} {} ORDER BY random() LIMIT 10000".format( + required_variables, ldf.table_name, where_clause + ) else: - query = "SELECT {} FROM {} {}".format(required_variables, ldf.table_name, where_clause) + query = "SELECT {} FROM {} {}".format( + required_variables, ldf.table_name, where_clause + ) data = pd.read_sql(query, ldf.SQLconnection) vis._vis_data = utils.pandas_to_lux(data) - if (vis.mark =="bar" or vis.mark =="line"): + if vis.mark == "bar" or vis.mark == "line": SQLExecutor.execute_aggregate(vis, ldf) - elif (vis.mark =="histogram"): + elif vis.mark == "histogram": SQLExecutor.execute_binning(vis, ldf) @staticmethod - def execute_aggregate(vis:Vis, ldf:LuxDataFrame): + def execute_aggregate(vis: Vis, ldf: LuxDataFrame): import pandas as pd + x_attr = vis.get_attr_by_channel("x")[0] y_attr = vis.get_attr_by_channel("y")[0] - groupby_attr ="" - measure_attr ="" - if (y_attr.aggregation!=""): + groupby_attr = "" + measure_attr = "" + if y_attr.aggregation != "": groupby_attr = x_attr measure_attr = y_attr agg_func = y_attr.aggregation - if (x_attr.aggregation!=""): + if x_attr.aggregation != "": groupby_attr = y_attr measure_attr = x_attr agg_func = x_attr.aggregation - - if (measure_attr!=""): - #barchart case, need count data for each group - if (measure_attr.attribute=="Record"): + + if measure_attr != "": + # barchart case, need count data for each group + if measure_attr.attribute == "Record": where_clause, filterVars = SQLExecutor.execute_filter(vis) - count_query = "SELECT {}, COUNT({}) FROM {} {} GROUP BY {}".format(groupby_attr.attribute, groupby_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + count_query = "SELECT {}, COUNT({}) FROM {} {} GROUP BY {}".format( + groupby_attr.attribute, + groupby_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + ) vis._vis_data = pd.read_sql(count_query, ldf.SQLconnection) - vis._vis_data = vis.data.rename(columns={"count":"Record"}) + vis._vis_data = vis.data.rename(columns={"count": "Record"}) vis._vis_data = utils.pandas_to_lux(vis.data) else: where_clause, filterVars = SQLExecutor.execute_filter(vis) if agg_func == "mean": - mean_query = "SELECT {}, AVG({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + mean_query = ( + "SELECT {}, AVG({}) as {} FROM {} {} GROUP BY {}".format( + groupby_attr.attribute, + measure_attr.attribute, + measure_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + ) + ) vis._vis_data = pd.read_sql(mean_query, ldf.SQLconnection) vis._vis_data = utils.pandas_to_lux(vis.data) if agg_func == "sum": - mean_query = "SELECT {}, SUM({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + mean_query = ( + "SELECT {}, SUM({}) as {} FROM {} {} GROUP BY {}".format( + groupby_attr.attribute, + measure_attr.attribute, + measure_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + ) + ) vis._vis_data = pd.read_sql(mean_query, ldf.SQLconnection) vis._vis_data = utils.pandas_to_lux(vis.data) if agg_func == "max": - mean_query = "SELECT {}, MAX({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + mean_query = ( + "SELECT {}, MAX({}) as {} FROM {} {} GROUP BY {}".format( + groupby_attr.attribute, + measure_attr.attribute, + measure_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + ) + ) vis._vis_data = pd.read_sql(mean_query, ldf.SQLconnection) vis._vis_data = utils.pandas_to_lux(vis.data) - #pad empty categories with 0 counts after filter is applied + # pad empty categories with 0 counts after filter is applied all_attr_vals = ldf.unique_values[groupby_attr.attribute] result_vals = list(vis.data[groupby_attr.attribute]) - if (len(result_vals) != len(all_attr_vals)): + if len(result_vals) != len(all_attr_vals): # For filtered aggregation that have missing groupby-attribute values, set these aggregated value as 0, since no datapoints for vals in all_attr_vals: - if (vals not in result_vals): - vis.data.loc[len(vis.data)] = [vals]+[0]*(len(vis.data.columns)-1) + if vals not in result_vals: + vis.data.loc[len(vis.data)] = [vals] + [0] * ( + len(vis.data.columns) - 1 + ) + @staticmethod - def execute_binning(vis:Vis, ldf:LuxDataFrame): + def execute_binning(vis: Vis, ldf: LuxDataFrame): import numpy as np import pandas as pd - bin_attribute = list(filter(lambda x: x.bin_size!=0,vis._inferred_intent))[0] - if not math.isnan(vis.data.min_max[bin_attribute.attribute][0]) and math.isnan(vis.data.min_max[bin_attribute.attribute][1]): + + bin_attribute = list(filter(lambda x: x.bin_size != 0, vis._inferred_intent))[0] + if not math.isnan(vis.data.min_max[bin_attribute.attribute][0]) and math.isnan( + vis.data.min_max[bin_attribute.attribute][1] + ): num_bins = bin_attribute.bin_size attr_min = min(ldf.unique_values[bin_attribute.attribute]) attr_max = max(ldf.unique_values[bin_attribute.attribute]) attr_type = type(ldf.unique_values[bin_attribute.attribute][0]) - #need to calculate the bin edges before querying for the relevant data - bin_width = (attr_max-attr_min)/num_bins + # need to calculate the bin edges before querying for the relevant data + bin_width = (attr_max - attr_min) / num_bins upper_edges = [] for e in range(1, num_bins): - curr_edge = attr_min + e*bin_width + curr_edge = attr_min + e * bin_width if attr_type == int: upper_edges.append(str(math.ceil(curr_edge))) else: upper_edges.append(str(curr_edge)) upper_edges = ",".join(upper_edges) vis_filter, filter_vars = SQLExecutor.execute_filter(vis) - bin_count_query = "SELECT width_bucket, COUNT(width_bucket) FROM (SELECT width_bucket({}, '{}') FROM {}) as Buckets GROUP BY width_bucket ORDER BY width_bucket".format(bin_attribute.attribute, '{'+upper_edges+'}', ldf.table_name) + bin_count_query = "SELECT width_bucket, COUNT(width_bucket) FROM (SELECT width_bucket({}, '{}') FROM {}) as Buckets GROUP BY width_bucket ORDER BY width_bucket".format( + bin_attribute.attribute, "{" + upper_edges + "}", ldf.table_name + ) bin_count_data = pd.read_sql(bin_count_query, ldf.SQLconnection) - #counts,binEdges = np.histogram(ldf[bin_attribute.attribute],bins=bin_attribute.bin_size) - #binEdges of size N+1, so need to compute binCenter as the bin location + # counts,binEdges = np.histogram(ldf[bin_attribute.attribute],bins=bin_attribute.bin_size) + # binEdges of size N+1, so need to compute binCenter as the bin location upper_edges = [float(i) for i in upper_edges.split(",")] if attr_type == int: - bin_centers = np.array([math.ceil((attr_min+attr_min+bin_width)/2)]) + bin_centers = np.array( + [math.ceil((attr_min + attr_min + bin_width) / 2)] + ) else: - bin_centers = np.array([(attr_min+attr_min+bin_width)/2]) - bin_centers = np.append(bin_centers, np.mean(np.vstack([upper_edges[0:-1],upper_edges[1:]]), axis=0)) + bin_centers = np.array([(attr_min + attr_min + bin_width) / 2]) + bin_centers = np.append( + bin_centers, + np.mean(np.vstack([upper_edges[0:-1], upper_edges[1:]]), axis=0), + ) if attr_type == int: - bin_centers = np.append(bin_centers, math.ceil((upper_edges[len(upper_edges)-1]+attr_max)/2)) + bin_centers = np.append( + bin_centers, + math.ceil((upper_edges[len(upper_edges) - 1] + attr_max) / 2), + ) else: - bin_centers = np.append(bin_centers, (upper_edges[len(upper_edges)-1]+attr_max)/2) + bin_centers = np.append( + bin_centers, (upper_edges[len(upper_edges) - 1] + attr_max) / 2 + ) if len(bin_centers) > len(bin_count_data): - bucket_lables = bin_count_data['width_bucket'].unique() - for i in range(0,len(bin_centers)): + bucket_lables = bin_count_data["width_bucket"].unique() + for i in range(0, len(bin_centers)): if i not in bucket_lables: - bin_count_data = bin_count_data.append(pd.DataFrame([[i,0]], columns = bin_count_data.columns)) - vis._vis_data = pd.DataFrame(np.array([bin_centers,list(bin_count_data['count'])]).T,columns=[bin_attribute.attribute, "Number of Records"]) + bin_count_data = bin_count_data.append( + pd.DataFrame([[i, 0]], columns=bin_count_data.columns) + ) + vis._vis_data = pd.DataFrame( + np.array([bin_centers, list(bin_count_data["count"])]).T, + columns=[bin_attribute.attribute, "Number of Records"], + ) vis._vis_data = utils.pandas_to_lux(vis.data) - + @staticmethod - #takes in a vis and returns an appropriate SQL WHERE clause that based on the filters specified in the vis's _inferred_intent - def execute_filter(vis:Vis): + # takes in a vis and returns an appropriate SQL WHERE clause that based on the filters specified in the vis's _inferred_intent + def execute_filter(vis: Vis): where_clause = [] filters = utils.get_filter_specs(vis._inferred_intent) filter_vars = [] - if (filters): - for f in range(0,len(filters)): + if filters: + for f in range(0, len(filters)): if f == 0: where_clause.append("WHERE") else: where_clause.append("AND") - where_clause.extend([str(filters[f].attribute), str(filters[f].filter_op), "'" + str(filters[f].value) + "'"]) + where_clause.extend( + [ + str(filters[f].attribute), + str(filters[f].filter_op), + "'" + str(filters[f].value) + "'", + ] + ) if filters[f].attribute not in filter_vars: filter_vars.append(filters[f].attribute) if where_clause == []: - return("", []) + return ("", []) else: where_clause = " ".join(where_clause) - return(where_clause, filter_vars) \ No newline at end of file + return (where_clause, filter_vars) diff --git a/lux/executor/__init__.py b/lux/executor/__init__.py index cbfa9f5b..948becf5 100644 --- a/lux/executor/__init__.py +++ b/lux/executor/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -11,4 +11,3 @@ # 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. - diff --git a/lux/history/__init__.py b/lux/history/__init__.py index cbfa9f5b..948becf5 100644 --- a/lux/history/__init__.py +++ b/lux/history/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -11,4 +11,3 @@ # 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. - diff --git a/lux/history/event.py b/lux/history/event.py index 9313b8ae..48853aef 100644 --- a/lux/history/event.py +++ b/lux/history/event.py @@ -1,5 +1,5 @@ # 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 @@ -12,16 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -class Event(): - """ - Event represents a single operation applied to the dataframe, with input arguments of operation recorded - """ - def __init__(self,name,*args,**kwargs): - self.name = name - self.args = args - self.kwargs = kwargs - def __repr__(self): - if (self.args==() and self.kwargs=={}): - return f"" - else: - return f"" \ No newline at end of file + +class Event: + """ + Event represents a single operation applied to the dataframe, with input arguments of operation recorded + """ + + def __init__(self, name, *args, **kwargs): + self.name = name + self.args = args + self.kwargs = kwargs + + def __repr__(self): + if self.args == () and self.kwargs == {}: + return f"" + else: + return f"" diff --git a/lux/history/history.py b/lux/history/history.py index 4e78cbe5..602d0d11 100644 --- a/lux/history/history.py +++ b/lux/history/history.py @@ -1,5 +1,5 @@ # 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 @@ -15,25 +15,32 @@ from __future__ import annotations from typing import List, Union, Callable, Dict from lux.history.event import Event -class History(): - """ - History maintains a list of past Pandas operations performed on the dataframe - Currently only supports custom overridden functions (head, tail, info, describe) - """ - def __init__(self): - self._events=[] - - def __getitem__(self, key): - return self._events[key] - def __setitem__(self, key, value): - self._events[key] = value - def __len__(self): - return len(self._events) - def __repr__(self): - event_repr=[] - for event in self._events: - event_repr.append(event.__repr__()) - return "["+'\n'.join(event_repr)+"]" - def append_event(self,name,*args,**kwargs): - event = Event(name,*args,**kwargs) - self._events.append(event) \ No newline at end of file + + +class History: + """ + History maintains a list of past Pandas operations performed on the dataframe + Currently only supports custom overridden functions (head, tail, info, describe) + """ + + def __init__(self): + self._events = [] + + def __getitem__(self, key): + return self._events[key] + + def __setitem__(self, key, value): + self._events[key] = value + + def __len__(self): + return len(self._events) + + def __repr__(self): + event_repr = [] + for event in self._events: + event_repr.append(event.__repr__()) + return "[" + "\n".join(event_repr) + "]" + + def append_event(self, name, *args, **kwargs): + event = Event(name, *args, **kwargs) + self._events.append(event) diff --git a/lux/interestingness/__init__.py b/lux/interestingness/__init__.py index cbfa9f5b..948becf5 100644 --- a/lux/interestingness/__init__.py +++ b/lux/interestingness/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -11,4 +11,3 @@ # 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. - diff --git a/lux/interestingness/interestingness.py b/lux/interestingness/interestingness.py index cc8c1927..9d175583 100644 --- a/lux/interestingness/interestingness.py +++ b/lux/interestingness/interestingness.py @@ -1,5 +1,5 @@ # 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 @@ -21,273 +21,324 @@ import numpy as np from pandas.api.types import is_datetime64_any_dtype as is_datetime from scipy.spatial.distance import euclidean -def interestingness(vis:Vis ,ldf:LuxDataFrame) -> int: - """ - Compute the interestingness score of the vis. - The interestingness metric is dependent on the vis type. - - Parameters - ---------- - vis : Vis - ldf : LuxDataFrame - - Returns - ------- - int - Interestingness Score - """ - - - if vis.data is None or len(vis.data)==0: - return -1 - # raise Exception("Vis.data needs to be populated before interestingness can be computed. Run Executor.execute(vis,ldf).") - - n_dim = 0 - n_msr = 0 - - filter_specs = utils.get_filter_specs(vis._inferred_intent) - vis_attrs_specs = utils.get_attrs_specs(vis._inferred_intent) - - record_attrs = list(filter(lambda x: x.attribute=="Record" and x.data_model=="measure", vis_attrs_specs)) - n_record = len(record_attrs) - for clause in vis_attrs_specs: - if (clause.attribute!="Record"): - if (clause.data_model == 'dimension'): - n_dim += 1 - if (clause.data_model == 'measure'): - n_msr += 1 - n_filter = len(filter_specs) - attr_specs = [clause for clause in vis_attrs_specs if clause.attribute != "Record"] - dimension_lst = vis.get_attr_by_data_model("dimension") - measure_lst = vis.get_attr_by_data_model("measure") - v_size = len(vis.data) - # Line/Bar Chart - #print("r:", n_record, "m:", n_msr, "d:",n_dim) - if (n_dim == 1 and (n_msr==0 or n_msr==1)): - if (v_size<2): return -1 - if (n_filter == 0): - return unevenness(vis, ldf, measure_lst, dimension_lst) - elif(n_filter==1): - return deviation_from_overall(vis, ldf, filter_specs, measure_lst[0].attribute) - # Histogram - elif (n_dim == 0 and n_msr == 1): - if (v_size<2): return -1 - if (n_filter == 0 and "Number of Records" in vis.data): - if "Number of Records" in vis.data: - v = vis.data["Number of Records"] - return skewness(v) - elif (n_filter == 1 and "Number of Records" in vis.data): - return deviation_from_overall(vis, ldf, filter_specs, "Number of Records") - return -1 - # Scatter Plot - elif (n_dim == 0 and n_msr == 2): - if (v_size<10): return -1 - if (vis.mark=="heatmap"): - return weighted_correlation(vis.data["xBinStart"],vis.data["yBinStart"],vis.data["count"]) - if (n_filter==1): - v_filter_size = get_filtered_size(filter_specs, vis.data) - sig = v_filter_size/v_size - else: - sig = 1 - return sig * monotonicity(vis,attr_specs) - # Scatterplot colored by Dimension - elif (n_dim == 1 and n_msr == 2): - if (v_size<10): return -1 - color_attr = vis.get_attr_by_channel("color")[0].attribute - - C = ldf.cardinality[color_attr] - if (C<40): - return 1/C - else: - return -1 - # Scatterplot colored by dimension - elif (n_dim== 1 and n_msr == 2): - return 0.2 - # Scatterplot colored by measure - elif (n_msr == 3): - return 0.1 - # colored line and barchart cases - elif (vis.mark == "line" and n_dim == 2): - return 0.15 - #for colored bar chart, scoring based on Chi-square test for independence score. - #gives higher scores to colored bar charts with fewer total categories as these charts are easier to read and thus more useful for users - elif (vis.mark == "bar" and n_dim == 2): - from scipy.stats import chi2_contingency - measure_column = vis.get_attr_by_data_model("measure")[0].attribute - dimension_columns = vis.get_attr_by_data_model("dimension") - - groupby_column = dimension_columns[0].attribute - color_column = dimension_columns[1].attribute - - contingency_table = [] - groupby_cardinality = ldf.cardinality[groupby_column] - groupby_unique_vals = ldf.unique_values[groupby_column] - for c in range(0, groupby_cardinality): - contingency_table.append(vis.data[vis.data[groupby_column] == groupby_unique_vals[c]][measure_column]) - score = 0.12 - #ValueError results if an entire column of the contingency table is 0, can happen if an applied filter results in - #a category having no counts - - try: - color_cardinality = ldf.cardinality[color_column] - #scale down score based on number of categories - chi2_score = chi2_contingency(contingency_table)[0]*0.9**(color_cardinality+groupby_cardinality) - score = min(0.10, chi2_score) - except ValueError: - pass - return(score) - # Default - else: - return -1 + + +def interestingness(vis: Vis, ldf: LuxDataFrame) -> int: + """ + Compute the interestingness score of the vis. + The interestingness metric is dependent on the vis type. + + Parameters + ---------- + vis : Vis + ldf : LuxDataFrame + + Returns + ------- + int + Interestingness Score + """ + + if vis.data is None or len(vis.data) == 0: + return -1 + # raise Exception("Vis.data needs to be populated before interestingness can be computed. Run Executor.execute(vis,ldf).") + + n_dim = 0 + n_msr = 0 + + filter_specs = utils.get_filter_specs(vis._inferred_intent) + vis_attrs_specs = utils.get_attrs_specs(vis._inferred_intent) + + record_attrs = list( + filter( + lambda x: x.attribute == "Record" and x.data_model == "measure", + vis_attrs_specs, + ) + ) + n_record = len(record_attrs) + for clause in vis_attrs_specs: + if clause.attribute != "Record": + if clause.data_model == "dimension": + n_dim += 1 + if clause.data_model == "measure": + n_msr += 1 + n_filter = len(filter_specs) + attr_specs = [clause for clause in vis_attrs_specs if clause.attribute != "Record"] + dimension_lst = vis.get_attr_by_data_model("dimension") + measure_lst = vis.get_attr_by_data_model("measure") + v_size = len(vis.data) + # Line/Bar Chart + # print("r:", n_record, "m:", n_msr, "d:",n_dim) + if n_dim == 1 and (n_msr == 0 or n_msr == 1): + if v_size < 2: + return -1 + if n_filter == 0: + return unevenness(vis, ldf, measure_lst, dimension_lst) + elif n_filter == 1: + return deviation_from_overall( + vis, ldf, filter_specs, measure_lst[0].attribute + ) + # Histogram + elif n_dim == 0 and n_msr == 1: + if v_size < 2: + return -1 + if n_filter == 0 and "Number of Records" in vis.data: + if "Number of Records" in vis.data: + v = vis.data["Number of Records"] + return skewness(v) + elif n_filter == 1 and "Number of Records" in vis.data: + return deviation_from_overall(vis, ldf, filter_specs, "Number of Records") + return -1 + # Scatter Plot + elif n_dim == 0 and n_msr == 2: + if v_size < 10: + return -1 + if vis.mark == "heatmap": + return weighted_correlation( + vis.data["xBinStart"], vis.data["yBinStart"], vis.data["count"] + ) + if n_filter == 1: + v_filter_size = get_filtered_size(filter_specs, vis.data) + sig = v_filter_size / v_size + else: + sig = 1 + return sig * monotonicity(vis, attr_specs) + # Scatterplot colored by Dimension + elif n_dim == 1 and n_msr == 2: + if v_size < 10: + return -1 + color_attr = vis.get_attr_by_channel("color")[0].attribute + + C = ldf.cardinality[color_attr] + if C < 40: + return 1 / C + else: + return -1 + # Scatterplot colored by dimension + elif n_dim == 1 and n_msr == 2: + return 0.2 + # Scatterplot colored by measure + elif n_msr == 3: + return 0.1 + # colored line and barchart cases + elif vis.mark == "line" and n_dim == 2: + return 0.15 + # for colored bar chart, scoring based on Chi-square test for independence score. + # gives higher scores to colored bar charts with fewer total categories as these charts are easier to read and thus more useful for users + elif vis.mark == "bar" and n_dim == 2: + from scipy.stats import chi2_contingency + + measure_column = vis.get_attr_by_data_model("measure")[0].attribute + dimension_columns = vis.get_attr_by_data_model("dimension") + + groupby_column = dimension_columns[0].attribute + color_column = dimension_columns[1].attribute + + contingency_table = [] + groupby_cardinality = ldf.cardinality[groupby_column] + groupby_unique_vals = ldf.unique_values[groupby_column] + for c in range(0, groupby_cardinality): + contingency_table.append( + vis.data[vis.data[groupby_column] == groupby_unique_vals[c]][ + measure_column + ] + ) + score = 0.12 + # ValueError results if an entire column of the contingency table is 0, can happen if an applied filter results in + # a category having no counts + + try: + color_cardinality = ldf.cardinality[color_column] + # scale down score based on number of categories + chi2_score = chi2_contingency(contingency_table)[0] * 0.9 ** ( + color_cardinality + groupby_cardinality + ) + score = min(0.10, chi2_score) + except ValueError: + pass + return score + # Default + else: + return -1 + + def get_filtered_size(filter_specs, ldf): - filter_intents = filter_specs[0] - result = PandasExecutor.apply_filter(ldf, filter_intents.attribute, filter_intents.filter_op, filter_intents.value) - return len(result) + filter_intents = filter_specs[0] + result = PandasExecutor.apply_filter( + ldf, filter_intents.attribute, filter_intents.filter_op, filter_intents.value + ) + return len(result) + + def skewness(v): - from scipy.stats import skew - return skew(v) + from scipy.stats import skew + + return skew(v) + def weighted_avg(x, w): - return np.average(x,weights=w) + return np.average(x, weights=w) + def weighted_cov(x, y, w): return np.sum(w * (x - weighted_avg(x, w)) * (y - weighted_avg(y, w))) / np.sum(w) + def weighted_correlation(x, y, w): # Based on https://en.wikipedia.org/wiki/Pearson_correlation_coefficient#Weighted_correlation_coefficient - return weighted_cov(x, y, w) / np.sqrt(weighted_cov(x, x, w) * weighted_cov(y, y, w)) - -def deviation_from_overall(vis:Vis, ldf:LuxDataFrame, filter_specs:list, msr_attribute:str) -> int: - """ - Difference in bar chart/histogram shape from overall chart - Note: this function assumes that the filtered vis.data is operating on the same range as the unfiltered vis.data. - - Parameters - ---------- - vis : Vis - ldf : LuxDataFrame - filter_specs : list - List of filters from the Vis - msr_attribute : str - The attribute name of the measure value of the chart - - Returns - ------- - int - Score describing how different the vis is from the overall vis - """ - v_filter_size = get_filtered_size(filter_specs, ldf) - v_size = len(vis.data) - v_filter = vis.data[msr_attribute] - total = v_filter.sum() - v_filter = v_filter/total # normalize by total to get ratio - if (total==0): return 0 - # Generate an "Overall" Vis (TODO: This is computed multiple times for every vis, alternative is to directly access df.current_vis but we do not have guaruntee that will always be unfiltered vis (in the non-Filter action scenario)) - import copy - unfiltered_vis = copy.copy(vis) - unfiltered_vis._inferred_intent = utils.get_attrs_specs(vis._inferred_intent) # Remove filters, keep only attribute intent - ldf.executor.execute([unfiltered_vis],ldf) - - v = unfiltered_vis.data[msr_attribute] - v = v/v.sum() - assert len(v) == len(v_filter), "Data for filtered and unfiltered vis have unequal length." - sig = v_filter_size/v_size #significance factor - # Euclidean distance as L2 function - - rankSig = 1 #category measure value ranking significance factor - #if the vis is a barchart, count how many categories' rank, based on measure value, changes after the filter is applied - if vis.mark == "bar": - dimList = vis.get_attr_by_data_model("dimension") - - #use Pandas rank function to calculate rank positions for each category - v_rank = unfiltered_vis.data.rank() - v_filter_rank = vis.data.rank() - #go through and count the number of ranking changes between the filtered and unfiltered data - numCategories = ldf.cardinality[dimList[0].attribute] - for r in range(0, numCategories-1): - if v_rank[msr_attribute][r] != v_filter_rank[msr_attribute][r]: - rankSig += 1 - #normalize ranking significance factor - rankSig = rankSig/numCategories - - from scipy.spatial.distance import euclidean - return sig*rankSig* euclidean(v, v_filter) - -def unevenness(vis:Vis, ldf:LuxDataFrame, measure_lst:list, dimension_lst:list) -> int: - """ - Measure the unevenness of a bar chart vis. - If a bar chart is highly uneven across the possible values, then it may be interesting. (e.g., USA produces lots of cars compared to Japan and Europe) - Likewise, if a bar chart shows that the measure is the same for any possible values the dimension attribute could take on, then it may not very informative. - (e.g., The cars produced across all Origins (Europe, Japan, and USA) has approximately the same average Acceleration.) - - Parameters - ---------- - vis : Vis - ldf : LuxDataFrame - measure_lst : list - List of measures - dimension_lst : list - List of dimensions - Returns - ------- - int - Score describing how uneven the bar chart is. - """ - v = vis.data[measure_lst[0].attribute] - v = v/v.sum() # normalize by total to get ratio - C = ldf.cardinality[dimension_lst[0].attribute] - D = (0.9) ** C # cardinality-based discounting factor - v_flat = pd.Series([1 / C] * len(v)) - if (is_datetime(v)): - v = v.astype('int') - return D * euclidean(v, v_flat) - -def mutual_information(v_x:list , v_y:list) -> int: - #Interestingness metric for two measure attributes - #Calculate maximal information coefficient (see Murphy pg 61) or Pearson's correlation - from sklearn.metrics import mutual_info_score - return mutual_info_score(v_x, v_y) - -def monotonicity(vis:Vis, attr_specs:list, ignore_identity:bool=True) ->int: - """ - Monotonicity measures there is a monotonic trend in the scatterplot, whether linear or not. - This score is computed as the square of the Spearman correlation coefficient, which is the Pearson correlation on the ranks of x and y. - See "Graph-Theoretic Scagnostics", Wilkinson et al 2005: https://research.tableau.com/sites/default/files/Wilkinson_Infovis-05.pdf - Parameters - ---------- - vis : Vis - attr_spec: list - List of attribute Clause objects - - ignore_identity: bool - Boolean flag to ignore items with the same x and y attribute (score as -1) - - Returns - ------- - int - Score describing the strength of monotonic relationship in vis - """ - from scipy.stats import spearmanr - msr1 = attr_specs[0].attribute - msr2 = attr_specs[1].attribute - - if(ignore_identity and msr1 == msr2): #remove if measures are the same - return -1 - v_x = vis.data[msr1] - v_y = vis.data[msr2] - - import warnings - with warnings.catch_warnings(): - warnings.filterwarnings('error') - try: - score = (spearmanr(v_x, v_y)[0]) ** 2 - except(RuntimeWarning): - # RuntimeWarning: invalid value encountered in true_divide (occurs when v_x and v_y are uniform, stdev in denominator is zero, leading to spearman's correlation as nan), ignore these cases. - score = -1 - - if pd.isnull(score): - return -1 - else: - return score - # import scipy.stats - # return abs(scipy.stats.pearsonr(v_x,v_y)[0]) + return weighted_cov(x, y, w) / np.sqrt( + weighted_cov(x, x, w) * weighted_cov(y, y, w) + ) + + +def deviation_from_overall( + vis: Vis, ldf: LuxDataFrame, filter_specs: list, msr_attribute: str +) -> int: + """ + Difference in bar chart/histogram shape from overall chart + Note: this function assumes that the filtered vis.data is operating on the same range as the unfiltered vis.data. + + Parameters + ---------- + vis : Vis + ldf : LuxDataFrame + filter_specs : list + List of filters from the Vis + msr_attribute : str + The attribute name of the measure value of the chart + + Returns + ------- + int + Score describing how different the vis is from the overall vis + """ + v_filter_size = get_filtered_size(filter_specs, ldf) + v_size = len(vis.data) + v_filter = vis.data[msr_attribute] + total = v_filter.sum() + v_filter = v_filter / total # normalize by total to get ratio + if total == 0: + return 0 + # Generate an "Overall" Vis (TODO: This is computed multiple times for every vis, alternative is to directly access df.current_vis but we do not have guaruntee that will always be unfiltered vis (in the non-Filter action scenario)) + import copy + + unfiltered_vis = copy.copy(vis) + unfiltered_vis._inferred_intent = utils.get_attrs_specs( + vis._inferred_intent + ) # Remove filters, keep only attribute intent + ldf.executor.execute([unfiltered_vis], ldf) + + v = unfiltered_vis.data[msr_attribute] + v = v / v.sum() + assert len(v) == len( + v_filter + ), "Data for filtered and unfiltered vis have unequal length." + sig = v_filter_size / v_size # significance factor + # Euclidean distance as L2 function + + rankSig = 1 # category measure value ranking significance factor + # if the vis is a barchart, count how many categories' rank, based on measure value, changes after the filter is applied + if vis.mark == "bar": + dimList = vis.get_attr_by_data_model("dimension") + + # use Pandas rank function to calculate rank positions for each category + v_rank = unfiltered_vis.data.rank() + v_filter_rank = vis.data.rank() + # go through and count the number of ranking changes between the filtered and unfiltered data + numCategories = ldf.cardinality[dimList[0].attribute] + for r in range(0, numCategories - 1): + if v_rank[msr_attribute][r] != v_filter_rank[msr_attribute][r]: + rankSig += 1 + # normalize ranking significance factor + rankSig = rankSig / numCategories + + from scipy.spatial.distance import euclidean + + return sig * rankSig * euclidean(v, v_filter) + + +def unevenness( + vis: Vis, ldf: LuxDataFrame, measure_lst: list, dimension_lst: list +) -> int: + """ + Measure the unevenness of a bar chart vis. + If a bar chart is highly uneven across the possible values, then it may be interesting. (e.g., USA produces lots of cars compared to Japan and Europe) + Likewise, if a bar chart shows that the measure is the same for any possible values the dimension attribute could take on, then it may not very informative. + (e.g., The cars produced across all Origins (Europe, Japan, and USA) has approximately the same average Acceleration.) + + Parameters + ---------- + vis : Vis + ldf : LuxDataFrame + measure_lst : list + List of measures + dimension_lst : list + List of dimensions + Returns + ------- + int + Score describing how uneven the bar chart is. + """ + v = vis.data[measure_lst[0].attribute] + v = v / v.sum() # normalize by total to get ratio + C = ldf.cardinality[dimension_lst[0].attribute] + D = (0.9) ** C # cardinality-based discounting factor + v_flat = pd.Series([1 / C] * len(v)) + if is_datetime(v): + v = v.astype("int") + return D * euclidean(v, v_flat) + + +def mutual_information(v_x: list, v_y: list) -> int: + # Interestingness metric for two measure attributes + # Calculate maximal information coefficient (see Murphy pg 61) or Pearson's correlation + from sklearn.metrics import mutual_info_score + + return mutual_info_score(v_x, v_y) + + +def monotonicity(vis: Vis, attr_specs: list, ignore_identity: bool = True) -> int: + """ + Monotonicity measures there is a monotonic trend in the scatterplot, whether linear or not. + This score is computed as the square of the Spearman correlation coefficient, which is the Pearson correlation on the ranks of x and y. + See "Graph-Theoretic Scagnostics", Wilkinson et al 2005: https://research.tableau.com/sites/default/files/Wilkinson_Infovis-05.pdf + Parameters + ---------- + vis : Vis + attr_spec: list + List of attribute Clause objects + + ignore_identity: bool + Boolean flag to ignore items with the same x and y attribute (score as -1) + + Returns + ------- + int + Score describing the strength of monotonic relationship in vis + """ + from scipy.stats import spearmanr + + msr1 = attr_specs[0].attribute + msr2 = attr_specs[1].attribute + + if ignore_identity and msr1 == msr2: # remove if measures are the same + return -1 + v_x = vis.data[msr1] + v_y = vis.data[msr2] + + import warnings + + with warnings.catch_warnings(): + warnings.filterwarnings("error") + try: + score = (spearmanr(v_x, v_y)[0]) ** 2 + except (RuntimeWarning): + # RuntimeWarning: invalid value encountered in true_divide (occurs when v_x and v_y are uniform, stdev in denominator is zero, leading to spearman's correlation as nan), ignore these cases. + score = -1 + + if pd.isnull(score): + return -1 + else: + return score + # import scipy.stats + # return abs(scipy.stats.pearsonr(v_x,v_y)[0]) diff --git a/lux/processor/Compiler.py b/lux/processor/Compiler.py index 17edb97a..0635f2de 100644 --- a/lux/processor/Compiler.py +++ b/lux/processor/Compiler.py @@ -1,5 +1,5 @@ # 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 @@ -22,407 +22,467 @@ import numpy as np import warnings + class Compiler: - ''' - Given a intent with underspecified inputs, compile the intent into fully specified visualizations for visualization. - ''' - - def __init__(self): - self.name = "Compiler" - - def __repr__(self): - return f"" - - @staticmethod - def compile_vis(ldf: LuxDataFrame,vis:Vis) -> VisList: - if (vis): - vis_collection = Compiler.populate_data_type_model(ldf, [vis]) # autofill data type/model information - vis_collection = Compiler.remove_all_invalid(vis_collection) # remove invalid visualizations from collection - for vis in vis_collection: - Compiler.determine_encoding(ldf, vis) # autofill viz related information - ldf._compiled=True - return vis_collection - @staticmethod - def compile_intent(ldf: LuxDataFrame,_inferred_intent:List[Clause]) -> VisList: - """ - Compiles input specifications in the intent of the ldf into a collection of lux.vis objects for visualization. - 1) Enumerate a collection of visualizations interested by the user to generate a vis list - 2) Expand underspecified specifications(lux.Clause) for each of the generated visualizations. - 3) Determine encoding properties for each vis - - Parameters - ---------- - ldf : lux.core.frame - LuxDataFrame with underspecified intent. - vis_collection : list[lux.vis.Vis] - empty list that will be populated with specified lux.Vis objects. - - Returns - ------- - vis_collection: list[lux.Vis] - vis list with compiled lux.Vis objects. - """ - if (_inferred_intent): - vis_collection = Compiler.enumerate_collection(_inferred_intent,ldf) - vis_collection = Compiler.populate_data_type_model(ldf, vis_collection) # autofill data type/model information - if len(vis_collection)>=1: - vis_collection = Compiler.remove_all_invalid(vis_collection) # remove invalid visualizations from collection - for vis in vis_collection: - Compiler.determine_encoding(ldf, vis) # autofill viz related information - ldf._compiled=True - return vis_collection - - @staticmethod - def enumerate_collection(_inferred_intent:List[Clause],ldf: LuxDataFrame) -> VisList: - """ - Given specifications that have been expanded thorught populateOptions, - recursively iterate over the resulting list combinations to generate a vis list. - - Parameters - ---------- - ldf : lux.core.frame - LuxDataFrame with underspecified intent. - - Returns - ------- - VisList: list[lux.Vis] - vis list with compiled lux.Vis objects. - """ - import copy - intent = Compiler.populate_wildcard_options(_inferred_intent, ldf) - attributes = intent['attributes'] - filters = intent['filters'] - if len(attributes) == 0 and len(filters) > 0: - return [] - - collection = [] - - # TODO: generate combinations of column attributes recursively by continuing to accumulate attributes for len(colAtrr) times - def combine(col_attrs, accum): - last = (len(col_attrs) == 1) - n = len(col_attrs[0]) - for i in range(n): - column_list = copy.deepcopy(accum + [col_attrs[0][i]]) - if last: - if len(filters) > 0: # if we have filters, generate combinations for each row. - for row in filters: - _inferred_intent = copy.deepcopy(column_list + [row]) - vis = Vis(_inferred_intent) - collection.append(vis) - else: - vis = Vis(column_list) - collection.append(vis) - else: - combine(col_attrs[1:], column_list) - combine(attributes, []) - return VisList(collection) - - @staticmethod - def populate_data_type_model(ldf, vis_collection) -> VisList: - """ - Given a underspecified Clause, populate the data_type and data_model information accordingly - - Parameters - ---------- - ldf : lux.core.frame - LuxDataFrame with underspecified intent - - vis_collection : list[lux.vis.Vis] - List of lux.Vis objects that will have their underspecified Clause details filled out. - Returns - ------- - vlist: VisList - vis list with compiled lux.Vis objects. - """ - # TODO: copy might not be neccesary - from lux.utils.date_utils import is_datetime_string - import copy - vlist = copy.deepcopy(vis_collection) # Preserve the original dobj - for vis in vlist: - for clause in vis._inferred_intent: - if (clause.description == "?"): - clause.description = "" - # TODO: Note that "and not is_datetime_string(clause.attribute))" is a temporary hack and breaks the `test_row_column_group` example - if (clause.attribute!="" and clause.attribute!="Record"):# and not is_datetime_string(clause.attribute): - if (clause.data_type == ""): - clause.data_type = ldf.data_type_lookup[clause.attribute] - if (clause.data_type=="id"): - clause.data_type = "nominal" - if (clause.data_model == ""): - clause.data_model = ldf.data_model_lookup[clause.attribute] - if (clause.value!=""): - if (vis.title == ""): #If user provided title for Vis, then don't override. - if(isinstance(clause.value,np.datetime64)): - chart_title = date_utils.date_formatter(clause.value,ldf) - else: - chart_title = clause.value - vis.title = f"{clause.attribute} {clause.filter_op} {chart_title}" - return vlist - - @staticmethod - def remove_all_invalid(vis_collection:VisList) -> VisList: - """ - Given an expanded vis list, remove all visualizations that are invalid. - Currently, the invalid visualizations are ones that contain two of the same attribute, no more than two temporal attributes, or overlapping attributes (same filter attribute and visualized attribute). - Parameters - ---------- - vis_collection : list[lux.vis.Vis] - empty list that will be populated with specified lux.Vis objects. - Returns - ------- - lux.vis.VisList - vis list with compiled lux.Vis objects. - """ - new_vc = [] - for vis in vis_collection: - num_temporal_specs = 0 - attribute_set = set() - for clause in vis._inferred_intent: - attribute_set.add(clause.attribute) - if clause.data_type == "temporal": - num_temporal_specs += 1 - all_distinct_specs = 0 == len(vis._inferred_intent) - len(attribute_set) - if num_temporal_specs < 2 and all_distinct_specs: - new_vc.append(vis) - # else: - # warnings.warn("\nThere is more than one duplicate attribute specified in the intent.\nPlease check your intent specification again.") - - return VisList(new_vc) - - @staticmethod - def determine_encoding(ldf: LuxDataFrame, vis: Vis): - ''' - Populates Vis with the appropriate mark type and channel information based on ShowMe logic - Currently support up to 3 dimensions or measures - - Parameters - ---------- - ldf : lux.core.frame - LuxDataFrame with underspecified intent - vis : lux.vis.Vis - - Returns - ------- - None - - Notes - ----- - Implementing automatic encoding from Tableau's VizQL - Mackinlay, J. D., Hanrahan, P., & Stolte, C. (2007). - Show Me: Automatic presentation for visual analysis. - IEEE Transactions on Visualization and Computer Graphics, 13(6), 1137–1144. - https://doi.org/10.1109/TVCG.2007.70594 - ''' - # Count number of measures and dimensions - ndim = 0 - nmsr = 0 - filters = [] - for clause in vis._inferred_intent: - if (clause.value==""): - if (clause.data_model == "dimension"): - ndim += 1 - elif (clause.data_model == "measure" and clause.attribute!="Record"): - nmsr += 1 - else: # preserve to add back to _inferred_intent later - filters.append(clause) - # Helper function (TODO: Move this into utils) - def line_or_bar(ldf, dimension:Clause, measure:Clause): - dim_type = dimension.data_type - # If no aggregation function is specified, then default as average - if (measure.aggregation==""): - measure.set_aggregation("mean") - if (dim_type == "temporal" or dim_type == "oridinal"): - return "line", {"x": dimension, "y": measure} - else: # unordered categorical - # if cardinality large than 5 then sort bars - if ldf.cardinality[dimension.attribute]>5: - dimension.sort = "ascending" - return "bar", {"x": measure, "y": dimension} - # ShowMe logic + additional heuristics - #count_col = Clause( attribute="count()", data_model="measure") - count_col = Clause( attribute="Record", aggregation="count", data_model="measure", data_type="quantitative") - auto_channel={} - if (ndim == 0 and nmsr == 1): - # Histogram with Count - measure = vis.get_attr_by_data_model("measure",exclude_record=True)[0] - if (len(vis.get_attr_by_attr_name("Record"))<0): - vis._inferred_intent.append(count_col) - # If no bin specified, then default as 10 - if (measure.bin_size == 0): - measure.bin_size = 10 - auto_channel = {"x": measure, "y": count_col} - vis._mark = "histogram" - elif (ndim == 1 and (nmsr == 0 or nmsr == 1)): - # Line or Bar Chart - if (nmsr == 0): - vis._inferred_intent.append(count_col) - dimension = vis.get_attr_by_data_model("dimension")[0] - measure = vis.get_attr_by_data_model("measure")[0] - vis._mark, auto_channel = line_or_bar(ldf, dimension, measure) - elif (ndim == 2 and (nmsr == 0 or nmsr == 1)): - # Line or Bar chart broken down by the dimension - dimensions = vis.get_attr_by_data_model("dimension") - d1 = dimensions[0] - d2 = dimensions[1] - if (ldf.cardinality[d1.attribute] < ldf.cardinality[d2.attribute]): - # d1.channel = "color" - vis.remove_column_from_spec(d1.attribute) - dimension = d2 - color_attr = d1 - else: - if (d1.attribute == d2.attribute): - vis._inferred_intent.pop(0) # if same attribute then remove_column_from_spec will remove both dims, we only want to remove one - else: - vis.remove_column_from_spec(d2.attribute) - dimension = d1 - color_attr = d2 - # Colored Bar/Line chart with Count as default measure - if not ldf.pre_aggregated: - if (nmsr == 0 and not ldf.pre_aggregated): - vis._inferred_intent.append(count_col) - measure = vis.get_attr_by_data_model("measure")[0] - vis._mark, auto_channel = line_or_bar(ldf, dimension, measure) - auto_channel["color"] = color_attr - elif (ndim == 0 and nmsr == 2): - # Scatterplot - vis._mark = "scatter" - vis._inferred_intent[0].set_aggregation(None) - vis._inferred_intent[1].set_aggregation(None) - auto_channel = {"x": vis._inferred_intent[0], - "y": vis._inferred_intent[1]} - elif (ndim == 1 and nmsr == 2): - # Scatterplot broken down by the dimension - measure = vis.get_attr_by_data_model("measure") - m1 = measure[0] - m2 = measure[1] - - vis._inferred_intent[0].set_aggregation(None) - vis._inferred_intent[1].set_aggregation(None) - - color_attr = vis.get_attr_by_data_model("dimension")[0] - vis.remove_column_from_spec(color_attr) - vis._mark = "scatter" - auto_channel = {"x": m1, - "y": m2, - "color": color_attr} - elif (ndim == 0 and nmsr == 3): - # Scatterplot with color - vis._mark = "scatter" - auto_channel = {"x": vis._inferred_intent[0], - "y": vis._inferred_intent[1], - "color": vis._inferred_intent[2]} - relevant_attributes = [auto_channel[channel].attribute for channel in auto_channel] - relevant_min_max = dict((attr, ldf._min_max[attr]) for attr in relevant_attributes if attr != "Record" and attr in ldf._min_max) - vis._min_max = relevant_min_max - if (auto_channel!={}): - vis = Compiler.enforce_specified_channel(vis, auto_channel) - vis._inferred_intent.extend(filters) # add back the preserved filters - @staticmethod - def enforce_specified_channel(vis: Vis, auto_channel: Dict[str, str]): - """ - Enforces that the channels specified in the Vis by users overrides the showMe autoChannels. - - Parameters - ---------- - vis : lux.vis.Vis - Input Vis without channel specification. - auto_channel : Dict[str,str] - Key-value pair in the form [channel: attributeName] specifying the showMe recommended channel location. - - Returns - ------- - vis : lux.vis.Vis - Vis with channel specification combining both original and auto_channel specification. - - Raises - ------ - ValueError - Ensures no more than one attribute is placed in the same channel. - """ - result_dict = {} # result of enforcing specified channel will be stored in result_dict - specified_dict = {} # specified_dict={"x":[],"y":[list of Dobj with y specified as channel]} - # create a dictionary of specified channels in the given dobj - for val in auto_channel.keys(): - specified_dict[val] = vis.get_attr_by_channel(val) - result_dict[val] = "" - # for every element, replace with what's in specified_dict if specified - for sVal, sAttr in specified_dict.items(): - if (len(sAttr) == 1): # if specified in dobj - # remove the specified channel from auto_channel (matching by value, since channel key may not be same) - for i in list(auto_channel.keys()): - if ((auto_channel[i].attribute == sAttr[0].attribute) - and (auto_channel[i].channel == sVal)): # need to ensure that the channel is the same (edge case when duplicate Cols with same attribute name) - auto_channel.pop(i) - break - sAttr[0].channel = sVal - result_dict[sVal] = sAttr[0] - elif (len(sAttr) > 1): - raise ValueError("There should not be more than one attribute specified in the same channel.") - # For the leftover channels that are still unspecified in result_dict, - # and the leftovers in the auto_channel specification, - # step through them together and fill it automatically. - leftover_channels = list(filter(lambda x: result_dict[x] == '', result_dict)) - for leftover_channel, leftover_encoding in zip(leftover_channels, auto_channel.values()): - leftover_encoding.channel = leftover_channel - result_dict[leftover_channel] = leftover_encoding - vis._inferred_intent = list(result_dict.values()) - return vis - - @staticmethod - # def populate_wildcard_options(ldf: LuxDataFrame) -> dict: - def populate_wildcard_options(_inferred_intent:List[Clause], ldf: LuxDataFrame) -> dict: - """ - Given wildcards and constraints in the LuxDataFrame's intent, - return the list of available values that satisfies the data_type or data_model constraints. - - Parameters - ---------- - ldf : LuxDataFrame - LuxDataFrame with row or attributes populated with available wildcard options. - - Returns - ------- - intent: Dict[str,list] - a dictionary that holds the attributes and filters generated from wildcards and constraints. - """ - import copy - from lux.utils.utils import convert_to_list - - intent = {"attributes": [], "filters": []} - for clause in _inferred_intent: - spec_options = [] - if clause.value == "": # attribute - if clause.attribute == "?": - options = set(list(ldf.columns)) # all attributes - if (clause.data_type != ""): - options = options.intersection(set(ldf.data_type[clause.data_type])) - if (clause.data_model != ""): - options = options.intersection(set(ldf.data_model[clause.data_model])) - options = list(options) - else: - options = convert_to_list(clause.attribute) - for optStr in options: - if str(optStr) not in clause.exclude: - spec_copy = copy.copy(clause) - spec_copy.attribute = optStr - spec_options.append(spec_copy) - intent["attributes"].append(spec_options) - else: # filters - attr_lst = convert_to_list(clause.attribute) - for attr in attr_lst: - options = [] - if clause.value == "?": - options = ldf.unique_values[attr] - specInd = _inferred_intent.index(clause) - _inferred_intent[specInd] = Clause(attribute=clause.attribute, filter_op="=", value=list(options)) - else: - options.extend(convert_to_list(clause.value)) - for optStr in options: - if str(optStr) not in clause.exclude: - spec_copy = copy.copy(clause) - spec_copy.attribute = attr - spec_copy.value = optStr - spec_options.append(spec_copy) - intent["filters"].extend(spec_options) - - return intent + """ + Given a intent with underspecified inputs, compile the intent into fully specified visualizations for visualization. + """ + + def __init__(self): + self.name = "Compiler" + + def __repr__(self): + return f"" + + @staticmethod + def compile_vis(ldf: LuxDataFrame, vis: Vis) -> VisList: + if vis: + vis_collection = Compiler.populate_data_type_model( + ldf, [vis] + ) # autofill data type/model information + vis_collection = Compiler.remove_all_invalid( + vis_collection + ) # remove invalid visualizations from collection + for vis in vis_collection: + Compiler.determine_encoding( + ldf, vis + ) # autofill viz related information + ldf._compiled = True + return vis_collection + + @staticmethod + def compile_intent(ldf: LuxDataFrame, _inferred_intent: List[Clause]) -> VisList: + """ + Compiles input specifications in the intent of the ldf into a collection of lux.vis objects for visualization. + 1) Enumerate a collection of visualizations interested by the user to generate a vis list + 2) Expand underspecified specifications(lux.Clause) for each of the generated visualizations. + 3) Determine encoding properties for each vis + + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent. + vis_collection : list[lux.vis.Vis] + empty list that will be populated with specified lux.Vis objects. + + Returns + ------- + vis_collection: list[lux.Vis] + vis list with compiled lux.Vis objects. + """ + if _inferred_intent: + vis_collection = Compiler.enumerate_collection(_inferred_intent, ldf) + vis_collection = Compiler.populate_data_type_model( + ldf, vis_collection + ) # autofill data type/model information + if len(vis_collection) >= 1: + vis_collection = Compiler.remove_all_invalid( + vis_collection + ) # remove invalid visualizations from collection + for vis in vis_collection: + Compiler.determine_encoding( + ldf, vis + ) # autofill viz related information + ldf._compiled = True + return vis_collection + + @staticmethod + def enumerate_collection( + _inferred_intent: List[Clause], ldf: LuxDataFrame + ) -> VisList: + """ + Given specifications that have been expanded thorught populateOptions, + recursively iterate over the resulting list combinations to generate a vis list. + + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent. + + Returns + ------- + VisList: list[lux.Vis] + vis list with compiled lux.Vis objects. + """ + import copy + + intent = Compiler.populate_wildcard_options(_inferred_intent, ldf) + attributes = intent["attributes"] + filters = intent["filters"] + if len(attributes) == 0 and len(filters) > 0: + return [] + + collection = [] + + # TODO: generate combinations of column attributes recursively by continuing to accumulate attributes for len(colAtrr) times + def combine(col_attrs, accum): + last = len(col_attrs) == 1 + n = len(col_attrs[0]) + for i in range(n): + column_list = copy.deepcopy(accum + [col_attrs[0][i]]) + if last: + if ( + len(filters) > 0 + ): # if we have filters, generate combinations for each row. + for row in filters: + _inferred_intent = copy.deepcopy(column_list + [row]) + vis = Vis(_inferred_intent) + collection.append(vis) + else: + vis = Vis(column_list) + collection.append(vis) + else: + combine(col_attrs[1:], column_list) + + combine(attributes, []) + return VisList(collection) + + @staticmethod + def populate_data_type_model(ldf, vis_collection) -> VisList: + """ + Given a underspecified Clause, populate the data_type and data_model information accordingly + + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent + + vis_collection : list[lux.vis.Vis] + List of lux.Vis objects that will have their underspecified Clause details filled out. + Returns + ------- + vlist: VisList + vis list with compiled lux.Vis objects. + """ + # TODO: copy might not be neccesary + from lux.utils.date_utils import is_datetime_string + import copy + + vlist = copy.deepcopy(vis_collection) # Preserve the original dobj + for vis in vlist: + for clause in vis._inferred_intent: + if clause.description == "?": + clause.description = "" + # TODO: Note that "and not is_datetime_string(clause.attribute))" is a temporary hack and breaks the `test_row_column_group` example + if ( + clause.attribute != "" and clause.attribute != "Record" + ): # and not is_datetime_string(clause.attribute): + if clause.data_type == "": + clause.data_type = ldf.data_type_lookup[clause.attribute] + if clause.data_type == "id": + clause.data_type = "nominal" + if clause.data_model == "": + clause.data_model = ldf.data_model_lookup[clause.attribute] + if clause.value != "": + if ( + vis.title == "" + ): # If user provided title for Vis, then don't override. + if isinstance(clause.value, np.datetime64): + chart_title = date_utils.date_formatter(clause.value, ldf) + else: + chart_title = clause.value + vis.title = ( + f"{clause.attribute} {clause.filter_op} {chart_title}" + ) + return vlist + + @staticmethod + def remove_all_invalid(vis_collection: VisList) -> VisList: + """ + Given an expanded vis list, remove all visualizations that are invalid. + Currently, the invalid visualizations are ones that contain two of the same attribute, no more than two temporal attributes, or overlapping attributes (same filter attribute and visualized attribute). + Parameters + ---------- + vis_collection : list[lux.vis.Vis] + empty list that will be populated with specified lux.Vis objects. + Returns + ------- + lux.vis.VisList + vis list with compiled lux.Vis objects. + """ + new_vc = [] + for vis in vis_collection: + num_temporal_specs = 0 + attribute_set = set() + for clause in vis._inferred_intent: + attribute_set.add(clause.attribute) + if clause.data_type == "temporal": + num_temporal_specs += 1 + all_distinct_specs = 0 == len(vis._inferred_intent) - len(attribute_set) + if num_temporal_specs < 2 and all_distinct_specs: + new_vc.append(vis) + # else: + # warnings.warn("\nThere is more than one duplicate attribute specified in the intent.\nPlease check your intent specification again.") + + return VisList(new_vc) + + @staticmethod + def determine_encoding(ldf: LuxDataFrame, vis: Vis): + """ + Populates Vis with the appropriate mark type and channel information based on ShowMe logic + Currently support up to 3 dimensions or measures + + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent + vis : lux.vis.Vis + + Returns + ------- + None + + Notes + ----- + Implementing automatic encoding from Tableau's VizQL + Mackinlay, J. D., Hanrahan, P., & Stolte, C. (2007). + Show Me: Automatic presentation for visual analysis. + IEEE Transactions on Visualization and Computer Graphics, 13(6), 1137–1144. + https://doi.org/10.1109/TVCG.2007.70594 + """ + # Count number of measures and dimensions + ndim = 0 + nmsr = 0 + filters = [] + for clause in vis._inferred_intent: + if clause.value == "": + if clause.data_model == "dimension": + ndim += 1 + elif clause.data_model == "measure" and clause.attribute != "Record": + nmsr += 1 + else: # preserve to add back to _inferred_intent later + filters.append(clause) + # Helper function (TODO: Move this into utils) + def line_or_bar(ldf, dimension: Clause, measure: Clause): + dim_type = dimension.data_type + # If no aggregation function is specified, then default as average + if measure.aggregation == "": + measure.set_aggregation("mean") + if dim_type == "temporal" or dim_type == "oridinal": + return "line", {"x": dimension, "y": measure} + else: # unordered categorical + # if cardinality large than 5 then sort bars + if ldf.cardinality[dimension.attribute] > 5: + dimension.sort = "ascending" + return "bar", {"x": measure, "y": dimension} + + # ShowMe logic + additional heuristics + # count_col = Clause( attribute="count()", data_model="measure") + count_col = Clause( + attribute="Record", + aggregation="count", + data_model="measure", + data_type="quantitative", + ) + auto_channel = {} + if ndim == 0 and nmsr == 1: + # Histogram with Count + measure = vis.get_attr_by_data_model("measure", exclude_record=True)[0] + if len(vis.get_attr_by_attr_name("Record")) < 0: + vis._inferred_intent.append(count_col) + # If no bin specified, then default as 10 + if measure.bin_size == 0: + measure.bin_size = 10 + auto_channel = {"x": measure, "y": count_col} + vis._mark = "histogram" + elif ndim == 1 and (nmsr == 0 or nmsr == 1): + # Line or Bar Chart + if nmsr == 0: + vis._inferred_intent.append(count_col) + dimension = vis.get_attr_by_data_model("dimension")[0] + measure = vis.get_attr_by_data_model("measure")[0] + vis._mark, auto_channel = line_or_bar(ldf, dimension, measure) + elif ndim == 2 and (nmsr == 0 or nmsr == 1): + # Line or Bar chart broken down by the dimension + dimensions = vis.get_attr_by_data_model("dimension") + d1 = dimensions[0] + d2 = dimensions[1] + if ldf.cardinality[d1.attribute] < ldf.cardinality[d2.attribute]: + # d1.channel = "color" + vis.remove_column_from_spec(d1.attribute) + dimension = d2 + color_attr = d1 + else: + if d1.attribute == d2.attribute: + vis._inferred_intent.pop( + 0 + ) # if same attribute then remove_column_from_spec will remove both dims, we only want to remove one + else: + vis.remove_column_from_spec(d2.attribute) + dimension = d1 + color_attr = d2 + # Colored Bar/Line chart with Count as default measure + if not ldf.pre_aggregated: + if nmsr == 0 and not ldf.pre_aggregated: + vis._inferred_intent.append(count_col) + measure = vis.get_attr_by_data_model("measure")[0] + vis._mark, auto_channel = line_or_bar(ldf, dimension, measure) + auto_channel["color"] = color_attr + elif ndim == 0 and nmsr == 2: + # Scatterplot + vis._mark = "scatter" + vis._inferred_intent[0].set_aggregation(None) + vis._inferred_intent[1].set_aggregation(None) + auto_channel = {"x": vis._inferred_intent[0], "y": vis._inferred_intent[1]} + elif ndim == 1 and nmsr == 2: + # Scatterplot broken down by the dimension + measure = vis.get_attr_by_data_model("measure") + m1 = measure[0] + m2 = measure[1] + + vis._inferred_intent[0].set_aggregation(None) + vis._inferred_intent[1].set_aggregation(None) + + color_attr = vis.get_attr_by_data_model("dimension")[0] + vis.remove_column_from_spec(color_attr) + vis._mark = "scatter" + auto_channel = {"x": m1, "y": m2, "color": color_attr} + elif ndim == 0 and nmsr == 3: + # Scatterplot with color + vis._mark = "scatter" + auto_channel = { + "x": vis._inferred_intent[0], + "y": vis._inferred_intent[1], + "color": vis._inferred_intent[2], + } + relevant_attributes = [ + auto_channel[channel].attribute for channel in auto_channel + ] + relevant_min_max = dict( + (attr, ldf._min_max[attr]) + for attr in relevant_attributes + if attr != "Record" and attr in ldf._min_max + ) + vis._min_max = relevant_min_max + if auto_channel != {}: + vis = Compiler.enforce_specified_channel(vis, auto_channel) + vis._inferred_intent.extend(filters) # add back the preserved filters + + @staticmethod + def enforce_specified_channel(vis: Vis, auto_channel: Dict[str, str]): + """ + Enforces that the channels specified in the Vis by users overrides the showMe autoChannels. + + Parameters + ---------- + vis : lux.vis.Vis + Input Vis without channel specification. + auto_channel : Dict[str,str] + Key-value pair in the form [channel: attributeName] specifying the showMe recommended channel location. + + Returns + ------- + vis : lux.vis.Vis + Vis with channel specification combining both original and auto_channel specification. + + Raises + ------ + ValueError + Ensures no more than one attribute is placed in the same channel. + """ + result_dict = ( + {} + ) # result of enforcing specified channel will be stored in result_dict + specified_dict = ( + {} + ) # specified_dict={"x":[],"y":[list of Dobj with y specified as channel]} + # create a dictionary of specified channels in the given dobj + for val in auto_channel.keys(): + specified_dict[val] = vis.get_attr_by_channel(val) + result_dict[val] = "" + # for every element, replace with what's in specified_dict if specified + for sVal, sAttr in specified_dict.items(): + if len(sAttr) == 1: # if specified in dobj + # remove the specified channel from auto_channel (matching by value, since channel key may not be same) + for i in list(auto_channel.keys()): + if (auto_channel[i].attribute == sAttr[0].attribute) and ( + auto_channel[i].channel == sVal + ): # need to ensure that the channel is the same (edge case when duplicate Cols with same attribute name) + auto_channel.pop(i) + break + sAttr[0].channel = sVal + result_dict[sVal] = sAttr[0] + elif len(sAttr) > 1: + raise ValueError( + "There should not be more than one attribute specified in the same channel." + ) + # For the leftover channels that are still unspecified in result_dict, + # and the leftovers in the auto_channel specification, + # step through them together and fill it automatically. + leftover_channels = list(filter(lambda x: result_dict[x] == "", result_dict)) + for leftover_channel, leftover_encoding in zip( + leftover_channels, auto_channel.values() + ): + leftover_encoding.channel = leftover_channel + result_dict[leftover_channel] = leftover_encoding + vis._inferred_intent = list(result_dict.values()) + return vis + + @staticmethod + # def populate_wildcard_options(ldf: LuxDataFrame) -> dict: + def populate_wildcard_options( + _inferred_intent: List[Clause], ldf: LuxDataFrame + ) -> dict: + """ + Given wildcards and constraints in the LuxDataFrame's intent, + return the list of available values that satisfies the data_type or data_model constraints. + + Parameters + ---------- + ldf : LuxDataFrame + LuxDataFrame with row or attributes populated with available wildcard options. + + Returns + ------- + intent: Dict[str,list] + a dictionary that holds the attributes and filters generated from wildcards and constraints. + """ + import copy + from lux.utils.utils import convert_to_list + + intent = {"attributes": [], "filters": []} + for clause in _inferred_intent: + spec_options = [] + if clause.value == "": # attribute + if clause.attribute == "?": + options = set(list(ldf.columns)) # all attributes + if clause.data_type != "": + options = options.intersection( + set(ldf.data_type[clause.data_type]) + ) + if clause.data_model != "": + options = options.intersection( + set(ldf.data_model[clause.data_model]) + ) + options = list(options) + else: + options = convert_to_list(clause.attribute) + for optStr in options: + if str(optStr) not in clause.exclude: + spec_copy = copy.copy(clause) + spec_copy.attribute = optStr + spec_options.append(spec_copy) + intent["attributes"].append(spec_options) + else: # filters + attr_lst = convert_to_list(clause.attribute) + for attr in attr_lst: + options = [] + if clause.value == "?": + options = ldf.unique_values[attr] + specInd = _inferred_intent.index(clause) + _inferred_intent[specInd] = Clause( + attribute=clause.attribute, + filter_op="=", + value=list(options), + ) + else: + options.extend(convert_to_list(clause.value)) + for optStr in options: + if str(optStr) not in clause.exclude: + spec_copy = copy.copy(clause) + spec_copy.attribute = attr + spec_copy.value = optStr + spec_options.append(spec_copy) + intent["filters"].extend(spec_options) + + return intent diff --git a/lux/processor/Parser.py b/lux/processor/Parser.py index 7127b841..c1852021 100644 --- a/lux/processor/Parser.py +++ b/lux/processor/Parser.py @@ -1,5 +1,5 @@ # 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 @@ -15,92 +15,103 @@ from lux.vis.Clause import Clause from lux.core.frame import LuxDataFrame from typing import List, Union + + class Parser: - """ - The parser takes in the user's input specifications (with string `description` fields), - then generates the Lux internal specification through lux.Clause. - """ - @staticmethod - def parse(intent: List[Union[Clause,str]]) -> List[Clause]: - """ - Given the string description from a list of input Clauses (intent), - assign the appropriate clause.attribute, clause.filter_op, and clause.value. - - Parameters - ---------- - intent : List[Clause] - Underspecified list of lux.Clause objects. + """ + The parser takes in the user's input specifications (with string `description` fields), + then generates the Lux internal specification through lux.Clause. + """ + + @staticmethod + def parse(intent: List[Union[Clause, str]]) -> List[Clause]: + """ + Given the string description from a list of input Clauses (intent), + assign the appropriate clause.attribute, clause.filter_op, and clause.value. + + Parameters + ---------- + intent : List[Clause] + Underspecified list of lux.Clause objects. + + Returns + ------- + List[Clause] + Parsed list of lux.Clause objects. + """ + if type(intent) != list: + raise TypeError( + "Input intent must be a list consisting of string descriptions or lux.Clause objects." + "\nSee more at: https://lux-api.readthedocs.io/en/latest/source/guide/intent.html" + ) + import re - Returns - ------- - List[Clause] - Parsed list of lux.Clause objects. - """ - if type(intent)!=list: - raise TypeError("Input intent must be a list consisting of string descriptions or lux.Clause objects." - "\nSee more at: https://lux-api.readthedocs.io/en/latest/source/guide/intent.html" - ) - import re - # intent = ldf.get_context() - new_context = [] - #checks for and converts users' string inputs into lux specifications - for clause in intent: - valid_values = [] - if isinstance(clause,list): - valid_values = [] - for v in clause: - if type(v) is str: # and v in list(ldf.columns): #TODO: Move validation check to Validator - valid_values.append(v) - temp_spec = Clause(attribute = valid_values) - new_context.append(temp_spec) - elif isinstance(clause,str): - #case where user specifies a filter - if "=" in clause: - eqInd = clause.index("=") - var = clause[0:eqInd] - if "|" in clause: - values = clause[eqInd+1:].split("|") - for v in values: - # if v in ldf.unique_values[var]: #TODO: Move validation check to Validator - valid_values.append(v) - else: - valid_values = clause[eqInd+1:] - # if var in list(ldf.columns): #TODO: Move validation check to Validator - temp_spec = Clause(attribute = var, filter_op = "=", value = valid_values) - new_context.append(temp_spec) - #case where user specifies a variable - else: - if "|" in clause: - values = clause.split("|") - for v in values: - # if v in list(ldf.columns): #TODO: Move validation check to Validator - valid_values.append(v) - else: - valid_values = clause - temp_spec = Clause(attribute = valid_values) - new_context.append(temp_spec) - elif type(clause) is Clause: - new_context.append(clause) - intent = new_context - # ldf._intent = new_context + # intent = ldf.get_context() + new_context = [] + # checks for and converts users' string inputs into lux specifications + for clause in intent: + valid_values = [] + if isinstance(clause, list): + valid_values = [] + for v in clause: + if ( + type(v) is str + ): # and v in list(ldf.columns): #TODO: Move validation check to Validator + valid_values.append(v) + temp_spec = Clause(attribute=valid_values) + new_context.append(temp_spec) + elif isinstance(clause, str): + # case where user specifies a filter + if "=" in clause: + eqInd = clause.index("=") + var = clause[0:eqInd] + if "|" in clause: + values = clause[eqInd + 1 :].split("|") + for v in values: + # if v in ldf.unique_values[var]: #TODO: Move validation check to Validator + valid_values.append(v) + else: + valid_values = clause[eqInd + 1 :] + # if var in list(ldf.columns): #TODO: Move validation check to Validator + temp_spec = Clause(attribute=var, filter_op="=", value=valid_values) + new_context.append(temp_spec) + # case where user specifies a variable + else: + if "|" in clause: + values = clause.split("|") + for v in values: + # if v in list(ldf.columns): #TODO: Move validation check to Validator + valid_values.append(v) + else: + valid_values = clause + temp_spec = Clause(attribute=valid_values) + new_context.append(temp_spec) + elif type(clause) is Clause: + new_context.append(clause) + intent = new_context + # ldf._intent = new_context - for clause in intent: - if (clause.description): - #TODO: Move validation check to Validator - #if ((clause.description in list(ldf.columns)) or clause.description == "?"):# if clause.description in the list of attributes - if any(ext in [">","<","=","!="] for ext in clause.description): # clause.description contain ">","<". or "=" - # then parse it and assign to clause.attribute, clause.filter_op, clause.values - clause.filter_op = re.findall(r'/.*/|>|=|<|>=|<=|!=', clause.description)[0] - split_description = clause.description.split(clause.filter_op) - clause.attribute = split_description[0] - clause.value = split_description[1] - if re.match(r'^-?\d+(?:\.\d+)?$', clause.value): - clause.value = float(clause.value) - elif (type(clause.description) == str): - clause.attribute = clause.description - elif (type(clause.description)==list): - clause.attribute = clause.description - # else: # then it is probably a value - # clause.values = clause.description - return intent - # ldf._intent = intent \ No newline at end of file + for clause in intent: + if clause.description: + # TODO: Move validation check to Validator + # if ((clause.description in list(ldf.columns)) or clause.description == "?"):# if clause.description in the list of attributes + if any( + ext in [">", "<", "=", "!="] for ext in clause.description + ): # clause.description contain ">","<". or "=" + # then parse it and assign to clause.attribute, clause.filter_op, clause.values + clause.filter_op = re.findall( + r"/.*/|>|=|<|>=|<=|!=", clause.description + )[0] + split_description = clause.description.split(clause.filter_op) + clause.attribute = split_description[0] + clause.value = split_description[1] + if re.match(r"^-?\d+(?:\.\d+)?$", clause.value): + clause.value = float(clause.value) + elif type(clause.description) == str: + clause.attribute = clause.description + elif type(clause.description) == list: + clause.attribute = clause.description + # else: # then it is probably a value + # clause.values = clause.description + return intent + # ldf._intent = intent diff --git a/lux/processor/Validator.py b/lux/processor/Validator.py index a3262a20..688a5f05 100644 --- a/lux/processor/Validator.py +++ b/lux/processor/Validator.py @@ -1,5 +1,5 @@ # 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 @@ -16,65 +16,85 @@ from lux.core.frame import LuxDataFrame from lux.vis.Clause import Clause from typing import List -from lux.utils.date_utils import is_datetime_series,is_datetime_string +from lux.utils.date_utils import is_datetime_series, is_datetime_string import warnings + + class Validator: - ''' - Contains methods for validating lux.Clause objects in the intent. - ''' - def __init__(self): - self.name = "Validator" + """ + Contains methods for validating lux.Clause objects in the intent. + """ + + def __init__(self): + self.name = "Validator" + + def __repr__(self): + return f"" - def __repr__(self): - return f"" + @staticmethod + def validate_intent(intent: List[Clause], ldf: LuxDataFrame) -> None: + """ + Validates input specifications from the user to find inconsistencies and errors. - @staticmethod - def validate_intent(intent: List[Clause], ldf:LuxDataFrame) -> None: - """ - Validates input specifications from the user to find inconsistencies and errors. + Parameters + ---------- + ldf : lux.core.frame + LuxDataFrame with underspecified intent. - Parameters - ---------- - ldf : lux.core.frame - LuxDataFrame with underspecified intent. + Returns + ------- + None - Returns - ------- - None + Raises + ------ + ValueError + Ensures input intent are consistent with DataFrame content. - Raises - ------ - ValueError - Ensures input intent are consistent with DataFrame content. - - """ + """ - def validate_clause(clause): - if not((clause.attribute and clause.attribute == "?") or (clause.value and clause.value=="?")): - if isinstance(clause.attribute,list): - for attr in clause.attribute: - if attr not in list(ldf.columns): - warnings.warn(f"The input attribute '{attr}' does not exist in the DataFrame.") - else: - if (clause.attribute!="Record"): - #we don't value check datetime since datetime can take filter values that don't exactly match the exact TimeStamp representation - if (clause.attribute and not is_datetime_string(clause.attribute)): - if not clause.attribute in list(ldf.columns): - warnings.warn(f"The input attribute '{clause.attribute}' does not exist in the DataFrame.") - if (clause.value and clause.attribute and clause.filter_op=="="): - series = ldf[clause.attribute] - if (not is_datetime_series(series)): - if isinstance(clause.value, list): - vals = clause.value - else: - vals = [clause.value] - for val in vals: - if (val not in series.values):#(not series.str.contains(val).any()): - warnings.warn(f"The input value '{val}' does not exist for the attribute '{clause.attribute}' for the DataFrame.") + def validate_clause(clause): + if not ( + (clause.attribute and clause.attribute == "?") + or (clause.value and clause.value == "?") + ): + if isinstance(clause.attribute, list): + for attr in clause.attribute: + if attr not in list(ldf.columns): + warnings.warn( + f"The input attribute '{attr}' does not exist in the DataFrame." + ) + else: + if clause.attribute != "Record": + # we don't value check datetime since datetime can take filter values that don't exactly match the exact TimeStamp representation + if clause.attribute and not is_datetime_string( + clause.attribute + ): + if not clause.attribute in list(ldf.columns): + warnings.warn( + f"The input attribute '{clause.attribute}' does not exist in the DataFrame." + ) + if ( + clause.value + and clause.attribute + and clause.filter_op == "=" + ): + series = ldf[clause.attribute] + if not is_datetime_series(series): + if isinstance(clause.value, list): + vals = clause.value + else: + vals = [clause.value] + for val in vals: + if ( + val not in series.values + ): # (not series.str.contains(val).any()): + warnings.warn( + f"The input value '{val}' does not exist for the attribute '{clause.attribute}' for the DataFrame." + ) - for clause in intent: - if type(clause) is list: - for s in clause: - validate_clause(s) - else: - validate_clause(clause) \ No newline at end of file + for clause in intent: + if type(clause) is list: + for s in clause: + validate_clause(s) + else: + validate_clause(clause) diff --git a/lux/processor/__init__.py b/lux/processor/__init__.py index cbfa9f5b..948becf5 100644 --- a/lux/processor/__init__.py +++ b/lux/processor/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -11,4 +11,3 @@ # 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. - diff --git a/lux/utils/__init__.py b/lux/utils/__init__.py index cbfa9f5b..948becf5 100644 --- a/lux/utils/__init__.py +++ b/lux/utils/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -11,4 +11,3 @@ # 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. - diff --git a/lux/utils/date_utils.py b/lux/utils/date_utils.py index 497db71e..eb067ea6 100644 --- a/lux/utils/date_utils.py +++ b/lux/utils/date_utils.py @@ -1,5 +1,5 @@ # 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 @@ -14,103 +14,121 @@ import pandas as pd -def date_formatter(time_stamp,ldf): - """ - Given a numpy timestamp and ldf, inspects which date granularity is appropriate and reformats timestamp accordingly - - Example - ---------- - For changing granularity the results differ as so. - days: '2020-01-01' -> '2020-1-1' - months: '2020-01-01' -> '2020-1' - years: '2020-01-01' -> '2020' - - Parameters - ---------- - time_stamp: np.datetime64 - timestamp object holding the date information - ldf : lux.core.frame - LuxDataFrame with a temporal field - - Returns - ------- - date_str: str - A reformatted version of the time_stamp according to granularity - """ - datetime = pd.to_datetime(time_stamp) - if ldf.data_type["temporal"]: - date_column = ldf[ldf.data_type["temporal"][0]] # assumes only one temporal column, may need to change this function to recieve multiple temporal columns in the future - granularity = compute_date_granularity(date_column) - date_str = "" - if granularity == "year": - date_str += str(datetime.year) - elif granularity == "month": - date_str += str(datetime.year)+ "-" + str(datetime.month) - elif granularity == "day": - date_str += str(datetime.year) +"-"+ str(datetime.month) +"-"+ str(datetime.day) - else: - # non supported granularity - return datetime.date() - - return date_str - - -def compute_date_granularity(date_column:pd.core.series.Series): - """ - Given a temporal column (pandas.core.series.Series), finds out the granularity of dates. - - Example - ---------- - ['2018-01-01', '2019-01-02', '2018-01-03'] -> "day" - ['2018-01-01', '2019-02-01', '2018-03-01'] -> "month" - ['2018-01-01', '2019-01-01', '2020-01-01'] -> "year" - - Parameters - ---------- - date_column: pandas.core.series.Series - Column series with datetime type - - Returns - ------- - field: str - A str specifying the granularity of dates for the inspected temporal column - """ - date_fields = ["day", "month", "year"] #supporting a limited set of Vega-Lite TimeUnit (https://vega.github.io/vega-lite/docs/timeunit.html) - date_index = pd.DatetimeIndex(date_column) - for field in date_fields: - if hasattr(date_index,field) and len(getattr(date_index, field).unique()) != 1 : #can be changed to sum(getattr(date_index, field)) != 0 - return field - return "year" #if none, then return year by default -def is_datetime_series(series:pd.Series) -> bool: - - """ - Check if the Series object is of datetime type - - Parameters - ---------- - series : pd.Series - - Returns - ------- - is_date: bool - """ - return pd.api.types.is_datetime64_any_dtype(series) or pd.api.types.is_period_dtype(series) -def is_datetime_string(string:str)-> bool: - """ - Check if the string is date-like. - - Parameters - ---------- - string : str - - Returns - ------- - is_date: bool - """ - from dateutil.parser import parse - try: - parse(string) - return True - - except ValueError: - return False \ No newline at end of file + +def date_formatter(time_stamp, ldf): + """ + Given a numpy timestamp and ldf, inspects which date granularity is appropriate and reformats timestamp accordingly + + Example + ---------- + For changing granularity the results differ as so. + days: '2020-01-01' -> '2020-1-1' + months: '2020-01-01' -> '2020-1' + years: '2020-01-01' -> '2020' + + Parameters + ---------- + time_stamp: np.datetime64 + timestamp object holding the date information + ldf : lux.core.frame + LuxDataFrame with a temporal field + + Returns + ------- + date_str: str + A reformatted version of the time_stamp according to granularity + """ + datetime = pd.to_datetime(time_stamp) + if ldf.data_type["temporal"]: + date_column = ldf[ + ldf.data_type["temporal"][0] + ] # assumes only one temporal column, may need to change this function to recieve multiple temporal columns in the future + granularity = compute_date_granularity(date_column) + date_str = "" + if granularity == "year": + date_str += str(datetime.year) + elif granularity == "month": + date_str += str(datetime.year) + "-" + str(datetime.month) + elif granularity == "day": + date_str += ( + str(datetime.year) + "-" + str(datetime.month) + "-" + str(datetime.day) + ) + else: + # non supported granularity + return datetime.date() + + return date_str + + +def compute_date_granularity(date_column: pd.core.series.Series): + """ + Given a temporal column (pandas.core.series.Series), finds out the granularity of dates. + + Example + ---------- + ['2018-01-01', '2019-01-02', '2018-01-03'] -> "day" + ['2018-01-01', '2019-02-01', '2018-03-01'] -> "month" + ['2018-01-01', '2019-01-01', '2020-01-01'] -> "year" + + Parameters + ---------- + date_column: pandas.core.series.Series + Column series with datetime type + + Returns + ------- + field: str + A str specifying the granularity of dates for the inspected temporal column + """ + date_fields = [ + "day", + "month", + "year", + ] # supporting a limited set of Vega-Lite TimeUnit (https://vega.github.io/vega-lite/docs/timeunit.html) + date_index = pd.DatetimeIndex(date_column) + for field in date_fields: + if ( + hasattr(date_index, field) and len(getattr(date_index, field).unique()) != 1 + ): # can be changed to sum(getattr(date_index, field)) != 0 + return field + return "year" # if none, then return year by default + + +def is_datetime_series(series: pd.Series) -> bool: + + """ + Check if the Series object is of datetime type + + Parameters + ---------- + series : pd.Series + + Returns + ------- + is_date: bool + """ + return pd.api.types.is_datetime64_any_dtype(series) or pd.api.types.is_period_dtype( + series + ) + + +def is_datetime_string(string: str) -> bool: + """ + Check if the string is date-like. + + Parameters + ---------- + string : str + + Returns + ------- + is_date: bool + """ + from dateutil.parser import parse + + try: + parse(string) + return True + + except ValueError: + return False diff --git a/lux/utils/message.py b/lux/utils/message.py index 57bf5dde..638fd581 100644 --- a/lux/utils/message.py +++ b/lux/utils/message.py @@ -1,5 +1,5 @@ # 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 @@ -12,23 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. + class Message: def __init__(self): self.messages = [] - def add_unique(self,item,priority=-1): - msg = {"text":item,"priority":priority} - if (msg not in self.messages): + + def add_unique(self, item, priority=-1): + msg = {"text": item, "priority": priority} + if msg not in self.messages: self.messages.append(msg) - def add(self,item,priority=-1): - self.messages.append({"text":item,"priority":priority}) + + def add(self, item, priority=-1): + self.messages.append({"text": item, "priority": priority}) + def to_html(self): - if (len(self.messages)==0): + if len(self.messages) == 0: return "" else: - sorted_msgs = sorted(self.messages, key = lambda i: i['priority'],reverse=True) + sorted_msgs = sorted( + self.messages, key=lambda i: i["priority"], reverse=True + ) html = "
    " for msg in sorted_msgs: msgTxt = msg["text"] - html+=f"
  • {msgTxt}
  • " + html += f"
  • {msgTxt}
  • " html += "
" - return html \ No newline at end of file + return html diff --git a/lux/utils/utils.py b/lux/utils/utils.py index e28d931b..148509db 100644 --- a/lux/utils/utils.py +++ b/lux/utils/utils.py @@ -1,5 +1,5 @@ # 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 @@ -12,59 +12,83 @@ # See the License for the specific language governing permissions and # limitations under the License. import pandas as pd + + def convert_to_list(x): - ''' - "a" --> ["a"] - ["a","b"] --> ["a","b"] - ''' - if type(x) != list: - return [x] - else: - return x + """ + "a" --> ["a"] + ["a","b"] --> ["a","b"] + """ + if type(x) != list: + return [x] + else: + return x + def pandas_to_lux(df): - from lux.core.frame import LuxDataFrame - values = df.values.tolist() - ldf = LuxDataFrame(values, columns = df.columns) - return(ldf) + from lux.core.frame import LuxDataFrame + + values = df.values.tolist() + ldf = LuxDataFrame(values, columns=df.columns) + return ldf + def get_attrs_specs(intent): - if (intent is None): return [] - spec_obj = list(filter(lambda x: x.value=="", intent)) - return spec_obj + if intent is None: + return [] + spec_obj = list(filter(lambda x: x.value == "", intent)) + return spec_obj + def get_filter_specs(intent): - if (intent is None): return [] - spec_obj = list(filter(lambda x: x.value!="", intent)) - return spec_obj + if intent is None: + return [] + spec_obj = list(filter(lambda x: x.value != "", intent)) + return spec_obj + def check_import_lux_widget(): - import pkgutil - if (pkgutil.find_loader("luxwidget") is None): - raise Exception("luxwidget is not installed. Run `pip install luxwidget' to install the Jupyter widget.\nSee more at: https://github.com/lux-org/lux-widget") + import pkgutil + + if pkgutil.find_loader("luxwidget") is None: + raise Exception( + "luxwidget is not installed. Run `pip install luxwidget' to install the Jupyter widget.\nSee more at: https://github.com/lux-org/lux-widget" + ) + def get_agg_title(clause): - if (clause.aggregation is None): - return f'{clause.attribute}' - elif (clause.attribute=="Record"): - return f'Number of Records' - else: - return f'{clause._aggregation_name.capitalize()} of {clause.attribute}' -def check_if_id_like(df,attribute): - import re - # Strong signals - high_cardinality = df.cardinality[attribute]>500 # so that aggregated reset_index fields don't get misclassified - attribute_contain_id = re.search(r'id',str(attribute)) is not None - almost_all_vals_unique = df.cardinality[attribute] >=0.98* len(df) - is_string = pd.api.types.is_string_dtype(df[attribute]) - if (is_string): - # For string IDs, usually serial numbers or codes with alphanumerics have a consistent length (eg., CG-39405) with little deviation. For a high cardinality string field but not ID field (like Name or Brand), there is less uniformity across the string lengths. - if (len(df)>50): - sampled = df[attribute].sample(50,random_state=99) - else: - sampled = df[attribute] - str_length_uniformity = sampled.apply(lambda x: type(x)==str and len(x)).std() < 3 - return high_cardinality and (attribute_contain_id or almost_all_vals_unique) and str_length_uniformity - else: - # TODO: Could probably add some type of entropy measure (since the binned id fields are usually very even) - return high_cardinality and (attribute_contain_id or almost_all_vals_unique) \ No newline at end of file + if clause.aggregation is None: + return f"{clause.attribute}" + elif clause.attribute == "Record": + return f"Number of Records" + else: + return f"{clause._aggregation_name.capitalize()} of {clause.attribute}" + + +def check_if_id_like(df, attribute): + import re + + # Strong signals + high_cardinality = ( + df.cardinality[attribute] > 500 + ) # so that aggregated reset_index fields don't get misclassified + attribute_contain_id = re.search(r"id", str(attribute)) is not None + almost_all_vals_unique = df.cardinality[attribute] >= 0.98 * len(df) + is_string = pd.api.types.is_string_dtype(df[attribute]) + if is_string: + # For string IDs, usually serial numbers or codes with alphanumerics have a consistent length (eg., CG-39405) with little deviation. For a high cardinality string field but not ID field (like Name or Brand), there is less uniformity across the string lengths. + if len(df) > 50: + sampled = df[attribute].sample(50, random_state=99) + else: + sampled = df[attribute] + str_length_uniformity = ( + sampled.apply(lambda x: type(x) == str and len(x)).std() < 3 + ) + return ( + high_cardinality + and (attribute_contain_id or almost_all_vals_unique) + and str_length_uniformity + ) + else: + # TODO: Could probably add some type of entropy measure (since the binned id fields are usually very even) + return high_cardinality and (attribute_contain_id or almost_all_vals_unique) diff --git a/lux/vis/Clause.py b/lux/vis/Clause.py index 2d313de3..b4faff52 100644 --- a/lux/vis/Clause.py +++ b/lux/vis/Clause.py @@ -1,5 +1,5 @@ # 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 @@ -13,120 +13,136 @@ # limitations under the License. import typing + + class Clause: - """ - Clause is the object representation of a single unit of the specification. - """ + """ + Clause is the object representation of a single unit of the specification. + """ + + def __init__( + self, + description: typing.Union[str, list] = "", + attribute: typing.Union[str, list] = "", + value: typing.Union[str, list] = "", + filter_op: str = "=", + channel: str = "", + data_type: str = "", + data_model: str = "", + aggregation: typing.Union[str, callable] = "", + bin_size: int = 0, + weight: float = 1, + sort: str = "", + exclude: typing.Union[str, list] = "", + ): + """ + + Parameters + ---------- + description : typing.Union[str,list], optional + Convenient shorthand description of specification, parser parses description into other properties (attribute, value, filter_op), by default "" + attribute : typing.Union[str,list], optional + Specified attribute(s) of interest, by default "" + By providing a list of attributes (e.g., [Origin,Brand]), user is interested in either one of the attribute (i.e., Origin or Brand). + value : typing.Union[str,list], optional + Specified value(s) of interest, by default "" + By providing a list of values (e.g., ["USA","Europe"]), user is interested in either one of the attribute (i.e., USA or Europe). + filter_op : str, optional + Filter operation of interest. + Possible values: '=', '<', '>', '<=', '>=', '!=', by default "=" + channel : str, optional + Encoding channel where the specified attribute should be placed. + Possible values: 'x','y','color', by default "" + data_type : str, optional + Data type for the specified attribute. + Possible values: 'nominal', 'quantitative','temporal', by default "" + data_model : str, optional + Data model for the specified attribute + Possible values: 'dimension', 'measure', by default "" + aggregation : typing.Union[str,callable], optional + Aggregation function for specified attribute, by default "" set as 'mean' + Possible values: 'sum','mean', and others string shorthand or functions supported by Pandas.aggregate (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.aggregate.html), including numpy aggregation functions (e.g., np.ptp), by default "" + Input `None` means no aggregation should be applied (e.g., data has been pre-aggregated) + bin_size : int, optional + Number of bins for histograms, by default 0 + weight : float, optional + A number between 0 and 1 indicating the importance of this Clause, by default 1 + sort : str, optional + Specifying whether and how the bar chart should be sorted + Possible values: 'ascending', 'descending', by default "" + """ + # Descriptor + self.description = description + # Description gets compiled to attribute, value, filter_op + self.attribute = attribute + self.value = value + self.filter_op = filter_op + # self.parseDescription() + # Properties + self.channel = channel + self.data_type = data_type + self.data_model = data_model + self.set_aggregation(aggregation) + self.bin_size = bin_size + self.weight = weight + self.sort = sort + self.exclude = exclude + + def get_attr(self): + return self.attribute - def __init__(self, description:typing.Union[str,list] ="",attribute: typing.Union[str,list] ="",value: typing.Union[str,list]="", - filter_op:str ="=", channel:str ="", data_type:str="",data_model:str="", - aggregation:typing.Union[str,callable] = "", bin_size:int=0, weight:float=1,sort:str="", exclude: typing.Union[str,list] =""): - """ + def copy_clause(self): + copied_clause = Clause() + copied_clause.__dict__ = self.__dict__.copy() # just a shallow copy + return copied_clause - Parameters - ---------- - description : typing.Union[str,list], optional - Convenient shorthand description of specification, parser parses description into other properties (attribute, value, filter_op), by default "" - attribute : typing.Union[str,list], optional - Specified attribute(s) of interest, by default "" - By providing a list of attributes (e.g., [Origin,Brand]), user is interested in either one of the attribute (i.e., Origin or Brand). - value : typing.Union[str,list], optional - Specified value(s) of interest, by default "" - By providing a list of values (e.g., ["USA","Europe"]), user is interested in either one of the attribute (i.e., USA or Europe). - filter_op : str, optional - Filter operation of interest. - Possible values: '=', '<', '>', '<=', '>=', '!=', by default "=" - channel : str, optional - Encoding channel where the specified attribute should be placed. - Possible values: 'x','y','color', by default "" - data_type : str, optional - Data type for the specified attribute. - Possible values: 'nominal', 'quantitative','temporal', by default "" - data_model : str, optional - Data model for the specified attribute - Possible values: 'dimension', 'measure', by default "" - aggregation : typing.Union[str,callable], optional - Aggregation function for specified attribute, by default "" set as 'mean' - Possible values: 'sum','mean', and others string shorthand or functions supported by Pandas.aggregate (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.aggregate.html), including numpy aggregation functions (e.g., np.ptp), by default "" - Input `None` means no aggregation should be applied (e.g., data has been pre-aggregated) - bin_size : int, optional - Number of bins for histograms, by default 0 - weight : float, optional - A number between 0 and 1 indicating the importance of this Clause, by default 1 - sort : str, optional - Specifying whether and how the bar chart should be sorted - Possible values: 'ascending', 'descending', by default "" - """ - # Descriptor - self.description = description - # Description gets compiled to attribute, value, filter_op - self.attribute = attribute - self.value = value - self.filter_op = filter_op - # self.parseDescription() - # Properties - self.channel = channel - self.data_type = data_type - self.data_model = data_model - self.set_aggregation(aggregation) - self.bin_size = bin_size - self.weight = weight - self.sort = sort - self.exclude = exclude + def set_aggregation(self, aggregation: typing.Union[str, callable]): + """ + Sets the aggregation function of Clause, + while updating _aggregation_name internally - def get_attr(self): - return self.attribute - - def copy_clause(self): - copied_clause = Clause() - copied_clause.__dict__ = self.__dict__.copy() # just a shallow copy - return(copied_clause) + Parameters + ---------- + aggregation : typing.Union[str,callable] + """ + self.aggregation = aggregation + # If aggregation input is a function (e.g., np.std), get the string name of the function for plotting + if hasattr(self.aggregation, "__name__"): + self._aggregation_name = self.aggregation.__name__ + else: + self._aggregation_name = self.aggregation - def set_aggregation(self,aggregation:typing.Union[str,callable]): - """ - Sets the aggregation function of Clause, - while updating _aggregation_name internally + def to_string(self): + if isinstance(self.attribute, list): + clauseStr = "|".join(self.attribute) + elif self.value == "": + clauseStr = self.attribute + else: + clauseStr = f"{self.attribute}{self.filter_op}{self.value}" + return clauseStr - Parameters - ---------- - aggregation : typing.Union[str,callable] - """ - self.aggregation = aggregation - # If aggregation input is a function (e.g., np.std), get the string name of the function for plotting - if hasattr(self.aggregation,'__name__'): - self._aggregation_name = self.aggregation.__name__ - else: - self._aggregation_name = self.aggregation - def to_string(self): - if isinstance(self.attribute,list): - clauseStr = '|'.join(self.attribute) - elif (self.value==""): - clauseStr = self.attribute - else: - clauseStr = f"{self.attribute}{self.filter_op}{self.value}" - return clauseStr - def __repr__(self): - attributes = [] - if self.description != "": - attributes.append(" description: " + self.description) - if self.channel != "": - attributes.append(" channel: " + self.channel) - if len(self.attribute) != 0: - attributes.append(" attribute: " + str(self.attribute)) - if self.filter_op != "=": - attributes.append(f" filter_op: {str(self.filter_op)}" ) - if self.aggregation != "" and self.aggregation is not None: - attributes.append(" aggregation: " + self._aggregation_name) - if self.value!="" or len(self.value) != 0 : - attributes.append(" value: " + str(self.value)) - if self.data_model != "": - attributes.append(" data_model: " + self.data_model) - if len(self.data_type) != 0: - attributes.append(" data_type: " + str(self.data_type)) - if self.bin_size != None: - attributes.append(" bin_size: " + str(self.bin_size)) - if len(self.exclude) != 0: - attributes.append(" exclude: " + str(self.exclude)) - attributes[0] = "" - filter_intents = None - channels, additional_channels = [], [] - for clause in self._inferred_intent: - - if hasattr(clause,"value"): - if clause.value != "": - filter_intents = clause - if hasattr(clause,"attribute"): - if clause.attribute != "": - if clause.aggregation != "" and clause.aggregation is not None: - attribute = clause._aggregation_name.upper() + "(" + clause.attribute + ")" - elif clause.bin_size > 0: - attribute = "BIN(" + clause.attribute + ")" - else: - attribute = clause.attribute - if clause.channel == "x": - channels.insert(0, [clause.channel, attribute]) - elif clause.channel == "y": - channels.insert(1, [clause.channel, attribute]) - elif clause.channel != "": - additional_channels.append([clause.channel, attribute]) - - channels.extend(additional_channels) - str_channels = "" - for channel in channels: - str_channels += channel[0] + ": " + channel[1] + ", " - - if filter_intents: - return f"" - else: - return f"" - @property - def data(self): - return self._vis_data - @property - def code(self): - return self._code - @property - def mark(self): - return self._mark - @property - def min_max(self): - return self._min_max - @property - def intent(self): - return self._intent - @intent.setter - def intent(self, intent:List[Clause]) -> None: - self.set_intent(intent) - def set_intent(self, intent:List[Clause]) -> None: - """ - Sets the intent of the Vis and refresh the source based on the new intent - - Parameters - ---------- - intent : List[Clause] - Query specifying the desired VisList - """ - self._intent = intent - self.refresh_source(self._source) - @property - def plot_config(self): - return self._plot_config - @plot_config.setter - def plot_config(self,config_func:Callable): - """ - Modify plot aesthetic settings to the Vis - Currently only supported for Altair visualizations - - Parameters - ---------- - config_func : typing.Callable - A function that takes in an AltairChart (https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html) as input and returns an AltairChart as output - """ - self._plot_config = config_func - def clear_plot_config(self): - self._plot_config = None - def _repr_html_(self): - from IPython.display import display - check_import_lux_widget() - import luxwidget - if (self.data is None): - raise Exception("No data is populated in Vis. In order to generate data required for the vis, use the 'refresh_source' function to populate the Vis with a data source (e.g., vis.refresh_source(df)).") - else: - from lux.core.frame import LuxDataFrame - widget = luxwidget.LuxWidget( - currentVis= LuxDataFrame.current_vis_to_JSON([self]), - recommendations=[], - intent="", - message = "" - ) - display(widget) - def get_attr_by_attr_name(self,attr_name): - return list(filter(lambda x: x.attribute == attr_name, self._inferred_intent)) - - def get_attr_by_channel(self, channel): - spec_obj = list(filter(lambda x: x.channel == channel and x.value=='' if hasattr(x, "channel") else False, self._inferred_intent)) - return spec_obj - - def get_attr_by_data_model(self, dmodel, exclude_record=False): - if (exclude_record): - return list(filter(lambda x: x.data_model == dmodel and x.value=='' if x.attribute!="Record" and hasattr(x, "data_model") else False, self._inferred_intent)) - else: - return list(filter(lambda x: x.data_model == dmodel and x.value=='' if hasattr(x, "data_model") else False, self._inferred_intent)) - - def get_attr_by_data_type(self, dtype): - return list(filter(lambda x: x.data_type == dtype and x.value=='' if hasattr(x, "data_type") else False, self._inferred_intent)) - - def remove_filter_from_spec(self, value): - new_intent = list(filter(lambda x: x.value != value, self._inferred_intent)) - self.set_intent(new_intent) - - def remove_column_from_spec(self, attribute, remove_first:bool=False): - """ - Removes an attribute from the Vis's clause - - Parameters - ---------- - attribute : str - attribute to be removed - remove_first : bool, optional - Boolean flag to determine whether to remove all instances of the attribute or only one (first) instance, by default False - """ - if (not remove_first): - new_inferred = list(filter(lambda x: x.attribute != attribute, self._inferred_intent)) - self._inferred_intent = new_inferred - self._intent = new_inferred - elif (remove_first): - new_inferred = [] - skip_check = False - for i in range(0, len(self._inferred_intent)): - if self._inferred_intent[i].value=="": # clause is type attribute - column_spec = [] - column_names = self._inferred_intent[i].attribute - # if only one variable in a column, columnName results in a string and not a list so - # you need to differentiate the cases - if isinstance(column_names, list): - for column in column_names: - if (column != attribute) or skip_check: - column_spec.append(column) - elif (remove_first): - remove_first = True - new_inferred.append(Clause(column_spec)) - else: - if column_names != attribute or skip_check: - new_inferred.append(Clause(attribute = column_names)) - elif (remove_first): - skip_check = True - else: - new_inferred.append(self._inferred_intent[i]) - self._intent = new_inferred - self._inferred_intent = new_inferred - - def to_Altair(self, standalone = False) -> str: - """ - Generate minimal Altair code to visualize the Vis - - Parameters - ---------- - standalone : bool, optional - Flag to determine if outputted code uses user-defined variable names or can be run independently, by default False - - Returns - ------- - str - String version of the Altair code. Need to print out the string to apply formatting. - """ - from lux.vislib.altair.AltairRenderer import AltairRenderer - renderer = AltairRenderer(output_type="Altair") - self._code= renderer.create_vis(self, standalone) - return self._code - - def to_VegaLite(self, prettyOutput = True) -> Union[dict,str]: - """ - Generate minimal Vega-Lite code to visualize the Vis - - Returns - ------- - Union[dict,str] - String or Dictionary of the VegaLite JSON specification - """ - import json - from lux.vislib.altair.AltairRenderer import AltairRenderer - renderer = AltairRenderer(output_type="VegaLite") - self._code = renderer.create_vis(self) - if (prettyOutput): - return "** Remove this comment -- Copy Text Below to Vega Editor(vega.github.io/editor) to visualize and edit **\n"+json.dumps(self._code, indent=2) - else: - return self._code - - def render_VSpec(self, renderer="altair"): - if (renderer == "altair"): - return self.to_VegaLite(prettyOutput=False) - - def refresh_source(self, ldf):# -> Vis: - """ - Loading the source data into the Vis by instantiating the specification and - populating the Vis based on the source data, effectively "materializing" the Vis. - - Parameters - ---------- - ldf : LuxDataframe - Input Dataframe to be attached to the Vis - - Returns - ------- - Vis - Complete Vis with fully-specified fields - - See Also - -------- - lux.Vis.VisList.refresh_source - - Note - ---- - Function derives a new _inferred_intent by instantiating the intent specification on the new data - """ - if (ldf is not None): - from lux.processor.Parser import Parser - from lux.processor.Validator import Validator - from lux.processor.Compiler import Compiler - from lux.executor.PandasExecutor import PandasExecutor #TODO: temporary (generalize to executor) - ldf.maintain_metadata() - self._source = ldf - self._inferred_intent = Parser.parse(self._intent) - Validator.validate_intent(self._inferred_intent,ldf) - vlist = Compiler.compile_vis(ldf,self) - ldf.executor.execute(vlist,ldf) - # Copying properties over since we can not redefine `self` within class function - if (len(vlist)>0): - vis = vlist[0] - self.title = vis.title - self._mark = vis._mark - self._inferred_intent = vis._inferred_intent - self._vis_data = vis.data - self._min_max = vis._min_max + """ + + def __init__(self, intent, source=None, title="", score=0.0): + self._intent = intent # This is the user's original intent to Vis + self._inferred_intent = intent # This is the re-written, expanded version of user's original intent (include inferred vis info) + self._source = source # This is the original data that is attached to the Vis + self._vis_data = None # This is the data that represents the Vis (e.g., selected, aggregated, binned) + self._code = None + self._mark = "" + self._min_max = {} + self._plot_config = None + self._postbin = None + self.title = title + self.score = score + self.refresh_source(self._source) + + def __repr__(self): + if self._source is None: + return ( + f"" + ) + filter_intents = None + channels, additional_channels = [], [] + for clause in self._inferred_intent: + + if hasattr(clause, "value"): + if clause.value != "": + filter_intents = clause + if hasattr(clause, "attribute"): + if clause.attribute != "": + if clause.aggregation != "" and clause.aggregation is not None: + attribute = ( + clause._aggregation_name.upper() + + "(" + + clause.attribute + + ")" + ) + elif clause.bin_size > 0: + attribute = "BIN(" + clause.attribute + ")" + else: + attribute = clause.attribute + if clause.channel == "x": + channels.insert(0, [clause.channel, attribute]) + elif clause.channel == "y": + channels.insert(1, [clause.channel, attribute]) + elif clause.channel != "": + additional_channels.append([clause.channel, attribute]) + + channels.extend(additional_channels) + str_channels = "" + for channel in channels: + str_channels += channel[0] + ": " + channel[1] + ", " + + if filter_intents: + return f"" + else: + return ( + f"" + ) + + @property + def data(self): + return self._vis_data + + @property + def code(self): + return self._code + + @property + def mark(self): + return self._mark + + @property + def min_max(self): + return self._min_max + + @property + def intent(self): + return self._intent + + @intent.setter + def intent(self, intent: List[Clause]) -> None: + self.set_intent(intent) + + def set_intent(self, intent: List[Clause]) -> None: + """ + Sets the intent of the Vis and refresh the source based on the new intent + + Parameters + ---------- + intent : List[Clause] + Query specifying the desired VisList + """ + self._intent = intent + self.refresh_source(self._source) + + @property + def plot_config(self): + return self._plot_config + + @plot_config.setter + def plot_config(self, config_func: Callable): + """ + Modify plot aesthetic settings to the Vis + Currently only supported for Altair visualizations + + Parameters + ---------- + config_func : typing.Callable + A function that takes in an AltairChart (https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html) as input and returns an AltairChart as output + """ + self._plot_config = config_func + + def clear_plot_config(self): + self._plot_config = None + + def _repr_html_(self): + from IPython.display import display + + check_import_lux_widget() + import luxwidget + + if self.data is None: + raise Exception( + "No data is populated in Vis. In order to generate data required for the vis, use the 'refresh_source' function to populate the Vis with a data source (e.g., vis.refresh_source(df))." + ) + else: + from lux.core.frame import LuxDataFrame + + widget = luxwidget.LuxWidget( + currentVis=LuxDataFrame.current_vis_to_JSON([self]), + recommendations=[], + intent="", + message="", + ) + display(widget) + + def get_attr_by_attr_name(self, attr_name): + return list(filter(lambda x: x.attribute == attr_name, self._inferred_intent)) + + def get_attr_by_channel(self, channel): + spec_obj = list( + filter( + lambda x: x.channel == channel and x.value == "" + if hasattr(x, "channel") + else False, + self._inferred_intent, + ) + ) + return spec_obj + + def get_attr_by_data_model(self, dmodel, exclude_record=False): + if exclude_record: + return list( + filter( + lambda x: x.data_model == dmodel and x.value == "" + if x.attribute != "Record" and hasattr(x, "data_model") + else False, + self._inferred_intent, + ) + ) + else: + return list( + filter( + lambda x: x.data_model == dmodel and x.value == "" + if hasattr(x, "data_model") + else False, + self._inferred_intent, + ) + ) + + def get_attr_by_data_type(self, dtype): + return list( + filter( + lambda x: x.data_type == dtype and x.value == "" + if hasattr(x, "data_type") + else False, + self._inferred_intent, + ) + ) + + def remove_filter_from_spec(self, value): + new_intent = list(filter(lambda x: x.value != value, self._inferred_intent)) + self.set_intent(new_intent) + + def remove_column_from_spec(self, attribute, remove_first: bool = False): + """ + Removes an attribute from the Vis's clause + + Parameters + ---------- + attribute : str + attribute to be removed + remove_first : bool, optional + Boolean flag to determine whether to remove all instances of the attribute or only one (first) instance, by default False + """ + if not remove_first: + new_inferred = list( + filter(lambda x: x.attribute != attribute, self._inferred_intent) + ) + self._inferred_intent = new_inferred + self._intent = new_inferred + elif remove_first: + new_inferred = [] + skip_check = False + for i in range(0, len(self._inferred_intent)): + if self._inferred_intent[i].value == "": # clause is type attribute + column_spec = [] + column_names = self._inferred_intent[i].attribute + # if only one variable in a column, columnName results in a string and not a list so + # you need to differentiate the cases + if isinstance(column_names, list): + for column in column_names: + if (column != attribute) or skip_check: + column_spec.append(column) + elif remove_first: + remove_first = True + new_inferred.append(Clause(column_spec)) + else: + if column_names != attribute or skip_check: + new_inferred.append(Clause(attribute=column_names)) + elif remove_first: + skip_check = True + else: + new_inferred.append(self._inferred_intent[i]) + self._intent = new_inferred + self._inferred_intent = new_inferred + + def to_Altair(self, standalone=False) -> str: + """ + Generate minimal Altair code to visualize the Vis + + Parameters + ---------- + standalone : bool, optional + Flag to determine if outputted code uses user-defined variable names or can be run independently, by default False + + Returns + ------- + str + String version of the Altair code. Need to print out the string to apply formatting. + """ + from lux.vislib.altair.AltairRenderer import AltairRenderer + + renderer = AltairRenderer(output_type="Altair") + self._code = renderer.create_vis(self, standalone) + return self._code + + def to_VegaLite(self, prettyOutput=True) -> Union[dict, str]: + """ + Generate minimal Vega-Lite code to visualize the Vis + + Returns + ------- + Union[dict,str] + String or Dictionary of the VegaLite JSON specification + """ + import json + from lux.vislib.altair.AltairRenderer import AltairRenderer + + renderer = AltairRenderer(output_type="VegaLite") + self._code = renderer.create_vis(self) + if prettyOutput: + return ( + "** Remove this comment -- Copy Text Below to Vega Editor(vega.github.io/editor) to visualize and edit **\n" + + json.dumps(self._code, indent=2) + ) + else: + return self._code + + def render_VSpec(self, renderer="altair"): + if renderer == "altair": + return self.to_VegaLite(prettyOutput=False) + + def refresh_source(self, ldf): # -> Vis: + """ + Loading the source data into the Vis by instantiating the specification and + populating the Vis based on the source data, effectively "materializing" the Vis. + + Parameters + ---------- + ldf : LuxDataframe + Input Dataframe to be attached to the Vis + + Returns + ------- + Vis + Complete Vis with fully-specified fields + + See Also + -------- + lux.Vis.VisList.refresh_source + + Note + ---- + Function derives a new _inferred_intent by instantiating the intent specification on the new data + """ + if ldf is not None: + from lux.processor.Parser import Parser + from lux.processor.Validator import Validator + from lux.processor.Compiler import Compiler + from lux.executor.PandasExecutor import ( + PandasExecutor, + ) # TODO: temporary (generalize to executor) + + ldf.maintain_metadata() + self._source = ldf + self._inferred_intent = Parser.parse(self._intent) + Validator.validate_intent(self._inferred_intent, ldf) + vlist = Compiler.compile_vis(ldf, self) + ldf.executor.execute(vlist, ldf) + # Copying properties over since we can not redefine `self` within class function + if len(vlist) > 0: + vis = vlist[0] + self.title = vis.title + self._mark = vis._mark + self._inferred_intent = vis._inferred_intent + self._vis_data = vis.data + self._min_max = vis._min_max diff --git a/lux/vis/VisList.py b/lux/vis/VisList.py index 1aec2f62..86ccf1a1 100644 --- a/lux/vis/VisList.py +++ b/lux/vis/VisList.py @@ -1,5 +1,5 @@ # 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 @@ -19,278 +19,331 @@ from lux.vis.Vis import Vis from lux.vis.Clause import Clause import warnings -class VisList(): - """VisList is a list of Vis objects. - """ - def __init__(self,input_lst:Union[List[Vis],List[Clause]],source=None): - # Overloaded Constructor - self._source = source - self._input_lst = input_lst - if len(input_lst)>0: - if (self._is_vis_input()): - self._collection = input_lst - self._intent = [] - else: - self._intent = input_lst - self._collection = [] - else: - self._collection = [] - self._intent = [] - self._widget = None - self.refresh_source(self._source) - @property - def intent(self): - return self._intent - @intent.setter - def intent(self, intent:List[Clause]) -> None: - self.set_intent(intent) - def set_intent(self, intent:List[Clause]) -> None: - """ - Sets the intent of the VisList and refresh the source based on the new clause - - Parameters - ---------- - intent : List[Clause] - Query specifying the desired VisList - """ - self._intent = intent - self.refresh_source(self._source) - @property - def exported(self) -> VisList: - """ - Get selected visualizations as exported Vis List - - Notes + + +class VisList: + """VisList is a list of Vis objects.""" + + def __init__(self, input_lst: Union[List[Vis], List[Clause]], source=None): + # Overloaded Constructor + self._source = source + self._input_lst = input_lst + if len(input_lst) > 0: + if self._is_vis_input(): + self._collection = input_lst + self._intent = [] + else: + self._intent = input_lst + self._collection = [] + else: + self._collection = [] + self._intent = [] + self._widget = None + self.refresh_source(self._source) + + @property + def intent(self): + return self._intent + + @intent.setter + def intent(self, intent: List[Clause]) -> None: + self.set_intent(intent) + + def set_intent(self, intent: List[Clause]) -> None: + """ + Sets the intent of the VisList and refresh the source based on the new clause + Parameters + ---------- + intent : List[Clause] + Query specifying the desired VisList + """ + self._intent = intent + self.refresh_source(self._source) + + @property + def exported(self) -> VisList: + """ + Get selected visualizations as exported Vis List + Notes ----- - Convert the _selectedVisIdxs dictionary into a programmable VisList - Example _selectedVisIdxs : - {'Vis List': [0, 2]} - - Returns - ------- - VisList - return a VisList of selected visualizations. -> VisList(v1, v2...) - """ - if not hasattr(self,"widget"): - warnings.warn( - "\nNo widget attached to the VisList." - "Please assign VisList to an output variable.\n" - "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips" - , stacklevel=2) - return [] - exported_vis_lst =self._widget._selectedVisIdxs - if (exported_vis_lst=={}): - warnings.warn( - "\nNo visualization selected to export.\n" - "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips" - ,stacklevel=2) - return [] - else: - exported_vis = VisList(list(map(self.__getitem__, exported_vis_lst["Vis List"]))) - return exported_vis - def remove_duplicates(self) -> None: - """ - Removes duplicate visualizations in Vis List - """ - self._collection = list(set(self._collection)) - - def remove_index(self, index): - self._collection.pop(index) - - def _is_vis_input(self): - if (type(self._input_lst[0])==Vis): - return True - elif (type(self._input_lst[0])==Clause): - return False - def __getitem__(self, key): - return self._collection[key] - def __setitem__(self, key, value): - self._collection[key] = value - def __len__(self): - return len(self._collection) - def __repr__(self): - if len(self._collection) == 0: - return str(self._input_lst) - x_channel = "" - y_channel = "" - largest_mark = 0 - largest_filter = 0 - for vis in self._collection: #finds longest x attribute among all visualizations - filter_intents = None - for clause in vis._inferred_intent: - if clause.value != "": - filter_intents = clause - - if (clause.aggregation != "" and clause.aggregation is not None): - attribute = clause._aggregation_name.upper() + "(" + clause.attribute + ")" - elif clause.bin_size > 0: - attribute = "BIN(" + clause.attribute + ")" - else: - attribute = clause.attribute - - if clause.channel == "x" and len(x_channel) < len(attribute): - x_channel = attribute - if clause.channel == "y" and len(y_channel) < len(attribute): - y_channel = attribute - if len(vis.mark) > largest_mark: - largest_mark = len(vis.mark) - if filter_intents and len(str(filter_intents.value)) + len(filter_intents.attribute) > largest_filter: - largest_filter = len(str(filter_intents.value)) + len(filter_intents.attribute) - vis_repr = [] - largest_x_length = len(x_channel) - largest_y_length = len(y_channel) - for vis in self._collection: #pads the shorter visualizations with spaces before the y attribute - filter_intents = None - x_channel = "" - y_channel = "" - additional_channels = [] - for clause in vis._inferred_intent: - if clause.value != "": - filter_intents = clause - - if (clause.aggregation != "" and clause.aggregation is not None and vis.mark!='scatter'): - attribute = clause._aggregation_name.upper() + "(" + clause.attribute + ")" - elif clause.bin_size > 0: - attribute = "BIN(" + clause.attribute + ")" - else: - attribute = clause.attribute - - if clause.channel == "x": - x_channel = attribute.ljust(largest_x_length) - elif clause.channel == "y": - y_channel = attribute - elif clause.channel != "": - additional_channels.append([clause.channel, attribute]) - if filter_intents: - y_channel = y_channel.ljust(largest_y_length) - elif largest_filter != 0: - y_channel = y_channel.ljust(largest_y_length + largest_filter + 9) - else: - y_channel = y_channel.ljust(largest_y_length + largest_filter) - if x_channel != "": - x_channel = "x: " + x_channel + ", " - if y_channel != "": - y_channel = "y: " + y_channel - aligned_mark = vis.mark.ljust(largest_mark) - str_additional_channels = "" - for channel in additional_channels: - str_additional_channels += ", " + channel[0] + ": " + channel[1] - if filter_intents: - aligned_filter = " -- [" + filter_intents.attribute + filter_intents.filter_op + str(filter_intents.value) + "]" - aligned_filter = aligned_filter.ljust(largest_filter + 8) - vis_repr.append(f" ") - else: - vis_repr.append(f" ") - return '['+',\n'.join(vis_repr)[1:]+']' - def map(self,function): - # generalized way of applying a function to each element - return map(function, self._collection) - - def get(self,field_name): - # Get the value of the field for all objects in the collection - def get_field(d_obj): - field_val = getattr(d_obj,field_name) - # Might want to write catch error if key not in field - return field_val - return self.map(get_field) - - def set(self,field_name,field_val): - return NotImplemented - def set_plot_config(self,config_func:Callable): - """ - Modify plot aesthetic settings to the Vis List - Currently only supported for Altair visualizations - - Parameters - ---------- - config_func : typing.Callable - A function that takes in an AltairChart (https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html) as input and returns an AltairChart as output - """ - for vis in self._collection: - vis.plot_config = config_func - def clear_plot_config(self): - for vis in self._collection: - vis.plot_config = None - def sort(self, remove_invalid=True, descending = True): - # remove the items that have invalid (-1) score - if (remove_invalid): self._collection = list(filter(lambda x: x.score!=-1,self._collection)) - # sort in-place by “score” by default if available, otherwise user-specified field to sort by - self._collection.sort(key=lambda x: x.score, reverse=descending) - - def topK(self,k): - #sort and truncate list to first K items - self.sort(remove_invalid=True) - return VisList(self._collection[:k]) - def bottomK(self,k): - #sort and truncate list to first K items - self.sort(descending=False,remove_invalid=True) - return VisList(self._collection[:k]) - def normalize_score(self, invert_order = False): - max_score = max(list(self.get("score"))) - for dobj in self._collection: - dobj.score = dobj.score/max_score - if (invert_order): dobj.score = 1 - dobj.score - def _repr_html_(self): - self._widget = None - from IPython.display import display - from lux.core.frame import LuxDataFrame - recommendation = {"action": "Vis List", - "description": "Shows a vis list defined by the intent"} - recommendation["collection"] = self._collection - - check_import_lux_widget() - import luxwidget - recJSON = LuxDataFrame.rec_to_JSON([recommendation]) - self._widget = luxwidget.LuxWidget( - currentVis={}, - recommendations=recJSON, - intent="", - message = "" - ) - display(self._widget) - - def refresh_source(self, ldf) : - """ - Loading the source into the visualizations in the VisList, then populating each visualization - based on the new source data, effectively "materializing" the visualization collection. - - Parameters - ---------- - ldf : LuxDataframe - Input Dataframe to be attached to the VisList - - Returns - ------- - VisList - Complete VisList with fully-specified fields - - See Also - -------- - lux.vis.Vis.refresh_source - - Note - ---- - Function derives a new _inferred_intent by instantiating the intent specification on the new data - """ - if (ldf is not None): - from lux.processor.Parser import Parser - from lux.processor.Validator import Validator - from lux.processor.Compiler import Compiler - self._source = ldf - self._source.maintain_metadata() - if len(self._input_lst)>0: - if (self._is_vis_input()): - compiled_collection = [] - for vis in self._collection: - vis._inferred_intent = Parser.parse(vis._intent) - Validator.validate_intent(vis._inferred_intent,ldf) - vislist = Compiler.compile_vis(ldf,vis) - if (len(vislist)>0): - vis = vislist[0] - compiled_collection.append(vis) - self._collection = compiled_collection - else: - self._inferred_intent = Parser.parse(self._intent) - Validator.validate_intent(self._inferred_intent,ldf) - self._collection = Compiler.compile_intent(ldf,self._inferred_intent) - ldf.executor.execute(self._collection,ldf) + Convert the _selectedVisIdxs dictionary into a programmable VisList + Example _selectedVisIdxs : + {'Vis List': [0, 2]} + + Returns + ------- + VisList + return a VisList of selected visualizations. -> VisList(v1, v2...) + """ + if not hasattr(self, "widget"): + warnings.warn( + "\nNo widget attached to the VisList." + "Please assign VisList to an output variable.\n" + "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips", + stacklevel=2, + ) + return [] + exported_vis_lst = self._widget._selectedVisIdxs + if exported_vis_lst == {}: + warnings.warn( + "\nNo visualization selected to export.\n" + "See more: https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips", + stacklevel=2, + ) + return [] + else: + exported_vis = VisList( + list(map(self.__getitem__, exported_vis_lst["Vis List"])) + ) + return exported_vis + + def remove_duplicates(self) -> None: + """ + Removes duplicate visualizations in Vis List + """ + self._collection = list(set(self._collection)) + + def remove_index(self, index): + self._collection.pop(index) + + def _is_vis_input(self): + if type(self._input_lst[0]) == Vis: + return True + elif type(self._input_lst[0]) == Clause: + return False + + def __getitem__(self, key): + return self._collection[key] + + def __setitem__(self, key, value): + self._collection[key] = value + + def __len__(self): + return len(self._collection) + + def __repr__(self): + if len(self._collection) == 0: + return str(self._input_lst) + x_channel = "" + y_channel = "" + largest_mark = 0 + largest_filter = 0 + for ( + vis + ) in self._collection: # finds longest x attribute among all visualizations + filter_intents = None + for clause in vis._inferred_intent: + if clause.value != "": + filter_intents = clause + + if clause.aggregation != "" and clause.aggregation is not None: + attribute = ( + clause._aggregation_name.upper() + "(" + clause.attribute + ")" + ) + elif clause.bin_size > 0: + attribute = "BIN(" + clause.attribute + ")" + else: + attribute = clause.attribute + + if clause.channel == "x" and len(x_channel) < len(attribute): + x_channel = attribute + if clause.channel == "y" and len(y_channel) < len(attribute): + y_channel = attribute + if len(vis.mark) > largest_mark: + largest_mark = len(vis.mark) + if ( + filter_intents + and len(str(filter_intents.value)) + len(filter_intents.attribute) + > largest_filter + ): + largest_filter = len(str(filter_intents.value)) + len( + filter_intents.attribute + ) + vis_repr = [] + largest_x_length = len(x_channel) + largest_y_length = len(y_channel) + for ( + vis + ) in ( + self._collection + ): # pads the shorter visualizations with spaces before the y attribute + filter_intents = None + x_channel = "" + y_channel = "" + additional_channels = [] + for clause in vis._inferred_intent: + if clause.value != "": + filter_intents = clause + + if ( + clause.aggregation != "" + and clause.aggregation is not None + and vis.mark != "scatter" + ): + attribute = ( + clause._aggregation_name.upper() + "(" + clause.attribute + ")" + ) + elif clause.bin_size > 0: + attribute = "BIN(" + clause.attribute + ")" + else: + attribute = clause.attribute + + if clause.channel == "x": + x_channel = attribute.ljust(largest_x_length) + elif clause.channel == "y": + y_channel = attribute + elif clause.channel != "": + additional_channels.append([clause.channel, attribute]) + if filter_intents: + y_channel = y_channel.ljust(largest_y_length) + elif largest_filter != 0: + y_channel = y_channel.ljust(largest_y_length + largest_filter + 9) + else: + y_channel = y_channel.ljust(largest_y_length + largest_filter) + if x_channel != "": + x_channel = "x: " + x_channel + ", " + if y_channel != "": + y_channel = "y: " + y_channel + aligned_mark = vis.mark.ljust(largest_mark) + str_additional_channels = "" + for channel in additional_channels: + str_additional_channels += ", " + channel[0] + ": " + channel[1] + if filter_intents: + aligned_filter = ( + " -- [" + + filter_intents.attribute + + filter_intents.filter_op + + str(filter_intents.value) + + "]" + ) + aligned_filter = aligned_filter.ljust(largest_filter + 8) + vis_repr.append( + f" " + ) + else: + vis_repr.append( + f" " + ) + return "[" + ",\n".join(vis_repr)[1:] + "]" + + def map(self, function): + # generalized way of applying a function to each element + return map(function, self._collection) + + def get(self, field_name): + # Get the value of the field for all objects in the collection + def get_field(d_obj): + field_val = getattr(d_obj, field_name) + # Might want to write catch error if key not in field + return field_val + + return self.map(get_field) + + def set(self, field_name, field_val): + return NotImplemented + + def set_plot_config(self, config_func: Callable): + """ + Modify plot aesthetic settings to the Vis List + Currently only supported for Altair visualizations + Parameters + ---------- + config_func : typing.Callable + A function that takes in an AltairChart (https://altair-viz.github.io/user_guide/generated/toplevel/altair.Chart.html) as input and returns an AltairChart as output + """ + for vis in self._collection: + vis.plot_config = config_func + + def clear_plot_config(self): + for vis in self._collection: + vis.plot_config = None + + def sort(self, remove_invalid=True, descending=True): + # remove the items that have invalid (-1) score + if remove_invalid: + self._collection = list(filter(lambda x: x.score != -1, self._collection)) + # sort in-place by “score” by default if available, otherwise user-specified field to sort by + self._collection.sort(key=lambda x: x.score, reverse=descending) + + def topK(self, k): + # sort and truncate list to first K items + self.sort(remove_invalid=True) + return VisList(self._collection[:k]) + + def bottomK(self, k): + # sort and truncate list to first K items + self.sort(descending=False, remove_invalid=True) + return VisList(self._collection[:k]) + + def normalize_score(self, invert_order=False): + max_score = max(list(self.get("score"))) + for dobj in self._collection: + dobj.score = dobj.score / max_score + if invert_order: + dobj.score = 1 - dobj.score + + def _repr_html_(self): + self._widget = None + from IPython.display import display + from lux.core.frame import LuxDataFrame + + recommendation = { + "action": "Vis List", + "description": "Shows a vis list defined by the intent", + } + recommendation["collection"] = self._collection + + check_import_lux_widget() + import luxwidget + + recJSON = LuxDataFrame.rec_to_JSON([recommendation]) + self._widget = luxwidget.LuxWidget( + currentVis={}, recommendations=recJSON, intent="", message="" + ) + display(self._widget) + + def refresh_source(self, ldf): + """ + Loading the source into the visualizations in the VisList, then populating each visualization + based on the new source data, effectively "materializing" the visualization collection. + Parameters + ---------- + ldf : LuxDataframe + Input Dataframe to be attached to the VisList + Returns + ------- + VisList + Complete VisList with fully-specified fields + + See Also + -------- + lux.vis.Vis.refresh_source + Note + ---- + Function derives a new _inferred_intent by instantiating the intent specification on the new data + """ + if ldf is not None: + from lux.processor.Parser import Parser + from lux.processor.Validator import Validator + from lux.processor.Compiler import Compiler + + self._source = ldf + self._source.maintain_metadata() + if len(self._input_lst) > 0: + if self._is_vis_input(): + compiled_collection = [] + for vis in self._collection: + vis._inferred_intent = Parser.parse(vis._intent) + Validator.validate_intent(vis._inferred_intent, ldf) + vislist = Compiler.compile_vis(ldf, vis) + if len(vislist) > 0: + vis = vislist[0] + compiled_collection.append(vis) + self._collection = compiled_collection + else: + self._inferred_intent = Parser.parse(self._intent) + Validator.validate_intent(self._inferred_intent, ldf) + self._collection = Compiler.compile_intent( + ldf, self._inferred_intent + ) + ldf.executor.execute(self._collection, ldf) diff --git a/lux/vis/__init__.py b/lux/vis/__init__.py index 33956083..0f58f612 100644 --- a/lux/vis/__init__.py +++ b/lux/vis/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .Clause import Clause \ No newline at end of file +from .Clause import Clause diff --git a/lux/vislib/__init__.py b/lux/vislib/__init__.py index cbfa9f5b..948becf5 100644 --- a/lux/vislib/__init__.py +++ b/lux/vislib/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -11,4 +11,3 @@ # 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. - diff --git a/lux/vislib/altair/AltairChart.py b/lux/vislib/altair/AltairChart.py index 0bf0c33e..09a01013 100644 --- a/lux/vislib/altair/AltairChart.py +++ b/lux/vislib/altair/AltairChart.py @@ -1,5 +1,5 @@ # 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 @@ -15,70 +15,105 @@ import pandas as pd import altair as alt from lux.utils.date_utils import compute_date_granularity + + class AltairChart: - """ - AltairChart is a representation of a chart. - Common utilities for charts that is independent of chart types should go here. + """ + AltairChart is a representation of a chart. + Common utilities for charts that is independent of chart types should go here. + + See Also + -------- + altair-viz.github.io + + """ + + def __init__(self, vis): + self.vis = vis + self.data = vis.data + self.tooltip = True + # ----- START self.code modification ----- + self.code = "" + self.chart = self.initialize_chart() + # self.add_tooltip() + self.encode_color() + self.add_title() + self.apply_default_config() + + # ----- END self.code modification ----- + + def __repr__(self): + return f"AltairChart <{str(self.vis)}>" + + def add_tooltip(self): + if self.tooltip: + self.chart = self.chart.encode(tooltip=list(self.vis.data.columns)) - See Also - -------- - altair-viz.github.io + def apply_default_config(self): + self.chart = self.chart.configure_title( + fontWeight=500, fontSize=13, font="Helvetica Neue" + ) + self.chart = self.chart.configure_axis( + titleFontWeight=500, + titleFontSize=11, + titleFont="Helvetica Neue", + labelFontWeight=400, + labelFontSize=9, + labelFont="Helvetica Neue", + labelColor="#505050", + ) + self.chart = self.chart.configure_legend( + titleFontWeight=500, + titleFontSize=10, + titleFont="Helvetica Neue", + labelFontWeight=400, + labelFontSize=9, + labelFont="Helvetica Neue", + ) + self.chart = self.chart.properties(width=160, height=150) + self.code += "\nchart = chart.configure_title(fontWeight=500,fontSize=13,font='Helvetica Neue')\n" + self.code += "chart = chart.configure_axis(titleFontWeight=500,titleFontSize=11,titleFont='Helvetica Neue',\n" + self.code += " labelFontWeight=400,labelFontSize=8,labelFont='Helvetica Neue',labelColor='#505050')\n" + self.code += "chart = chart.configure_legend(titleFontWeight=500,titleFontSize=10,titleFont='Helvetica Neue',\n" + self.code += ( + " labelFontWeight=400,labelFontSize=8,labelFont='Helvetica Neue')\n" + ) + self.code += "chart = chart.properties(width=160,height=150)\n" - """ - def __init__(self, vis): - self.vis = vis - self.data = vis.data - self.tooltip = True - # ----- START self.code modification ----- - self.code = "" - self.chart = self.initialize_chart() - # self.add_tooltip() - self.encode_color() - self.add_title() - self.apply_default_config() + def encode_color(self): + color_attr = self.vis.get_attr_by_channel("color") + if len(color_attr) == 1: + color_attr_name = color_attr[0].attribute + color_attr_type = color_attr[0].data_type + if color_attr_type == "temporal": + timeUnit = compute_date_granularity(self.vis.data[color_attr_name]) + self.chart = self.chart.encode( + color=alt.Color( + color_attr_name, + type=color_attr_type, + timeUnit=timeUnit, + title=color_attr_name, + ) + ) + self.code += f"chart = chart.encode(color=alt.Color('{color_attr_name}',type='{color_attr_type}',timeUnit='{timeUnit}',title='{color_attr_name}'))" + else: + self.chart = self.chart.encode( + color=alt.Color(color_attr_name, type=color_attr_type) + ) + self.code += f"chart = chart.encode(color=alt.Color('{color_attr_name}',type='{color_attr_type}'))\n" + elif len(color_attr) > 1: + raise ValueError( + "There should not be more than one attribute specified in the same channel." + ) - # ----- END self.code modification ----- - def __repr__(self): - return f"AltairChart <{str(self.vis)}>" - def add_tooltip(self): - if (self.tooltip): - self.chart = self.chart.encode(tooltip=list(self.vis.data.columns)) - def apply_default_config(self): - self.chart = self.chart.configure_title(fontWeight=500,fontSize=13,font="Helvetica Neue") - self.chart = self.chart.configure_axis(titleFontWeight=500,titleFontSize=11,titleFont="Helvetica Neue", - labelFontWeight=400,labelFontSize=9,labelFont="Helvetica Neue",labelColor="#505050") - self.chart = self.chart.configure_legend(titleFontWeight=500,titleFontSize=10,titleFont="Helvetica Neue", - labelFontWeight=400,labelFontSize=9,labelFont="Helvetica Neue") - self.chart = self.chart.properties(width=160,height=150) - self.code+= "\nchart = chart.configure_title(fontWeight=500,fontSize=13,font='Helvetica Neue')\n" - self.code+= "chart = chart.configure_axis(titleFontWeight=500,titleFontSize=11,titleFont='Helvetica Neue',\n" - self.code+= " labelFontWeight=400,labelFontSize=8,labelFont='Helvetica Neue',labelColor='#505050')\n" - self.code+= "chart = chart.configure_legend(titleFontWeight=500,titleFontSize=10,titleFont='Helvetica Neue',\n" - self.code+= " labelFontWeight=400,labelFontSize=8,labelFont='Helvetica Neue')\n" - self.code+= "chart = chart.properties(width=160,height=150)\n" + def add_title(self): + chart_title = self.vis.title + if chart_title: + self.chart = self.chart.encode().properties(title=chart_title) + if self.code != "": + self.code += ( + f"chart = chart.encode().properties(title = '{chart_title}')" + ) - def encode_color(self): - color_attr = self.vis.get_attr_by_channel("color") - if (len(color_attr)==1): - color_attr_name = color_attr[0].attribute - color_attr_type = color_attr[0].data_type - if (color_attr_type=="temporal"): - timeUnit = compute_date_granularity(self.vis.data[color_attr_name]) - self.chart = self.chart.encode(color=alt.Color(color_attr_name,type=color_attr_type,timeUnit=timeUnit,title=color_attr_name)) - self.code+=f"chart = chart.encode(color=alt.Color('{color_attr_name}',type='{color_attr_type}',timeUnit='{timeUnit}',title='{color_attr_name}'))" - else: - self.chart = self.chart.encode(color=alt.Color(color_attr_name,type=color_attr_type)) - self.code+=f"chart = chart.encode(color=alt.Color('{color_attr_name}',type='{color_attr_type}'))\n" - elif (len(color_attr)>1): - raise ValueError("There should not be more than one attribute specified in the same channel.") - - def add_title(self): - chart_title = self.vis.title - if chart_title: - self.chart = self.chart.encode().properties( - title = chart_title - ) - if (self.code!=""): - self.code+=f"chart = chart.encode().properties(title = '{chart_title}')" - def initialize_chart(self): - return NotImplemented + def initialize_chart(self): + return NotImplemented diff --git a/lux/vislib/altair/AltairRenderer.py b/lux/vislib/altair/AltairRenderer.py index 0e9ebe54..2692f72e 100644 --- a/lux/vislib/altair/AltairRenderer.py +++ b/lux/vislib/altair/AltairRenderer.py @@ -1,5 +1,5 @@ # 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 @@ -21,84 +21,111 @@ from lux.vislib.altair.Histogram import Histogram from lux.vislib.altair.Heatmap import Heatmap + class AltairRenderer: - """ - Renderer for Charts based on Altair (https://altair-viz.github.io/) - """ - def __init__(self,output_type="VegaLite"): - self.output_type = output_type - def __repr__(self): - return f"AltairRenderer" - def create_vis(self,vis, standalone=True): - """ - Input DataObject and return a visualization specification - - Parameters - ---------- - vis: lux.vis.Vis - Input Vis (with data) - standalone: bool - Flag to determine if outputted code uses user-defined variable names or can be run independently - Returns - ------- - chart : altair.Chart - Output Altair Chart Object - """ - # Lazy Evaluation for 2D Binning - if (vis.mark == "scatter" and vis._postbin): - vis._mark = "heatmap" - from lux.executor.PandasExecutor import PandasExecutor - PandasExecutor.execute_2D_binning(vis) - # If a column has a Period dtype, or contains Period objects, convert it back to Datetime - if vis.data is not None: - for attr in list(vis.data.columns): - if pd.api.types.is_period_dtype(vis.data.dtypes[attr]) or isinstance(vis.data[attr].iloc[0], pd.Period): - dateColumn = vis.data[attr] - vis.data[attr] = pd.PeriodIndex(dateColumn.values).to_timestamp() - if pd.api.types.is_interval_dtype(vis.data.dtypes[attr]) or isinstance(vis.data[attr].iloc[0], pd.Interval): - vis.data[attr] = vis.data[attr].astype(str) - if (vis.mark =="histogram"): - chart = Histogram(vis) - elif (vis.mark =="bar"): - chart = BarChart(vis) - elif (vis.mark =="scatter"): - chart = ScatterChart(vis) - elif (vis.mark =="line"): - chart = LineChart(vis) - elif (vis.mark =="heatmap"): - chart = Heatmap(vis) - else: - chart = None + """ + Renderer for Charts based on Altair (https://altair-viz.github.io/) + """ + + def __init__(self, output_type="VegaLite"): + self.output_type = output_type + + def __repr__(self): + return f"AltairRenderer" + + def create_vis(self, vis, standalone=True): + """ + Input DataObject and return a visualization specification + + Parameters + ---------- + vis: lux.vis.Vis + Input Vis (with data) + standalone: bool + Flag to determine if outputted code uses user-defined variable names or can be run independently + Returns + ------- + chart : altair.Chart + Output Altair Chart Object + """ + # Lazy Evaluation for 2D Binning + if vis.mark == "scatter" and vis._postbin: + vis._mark = "heatmap" + from lux.executor.PandasExecutor import PandasExecutor + + PandasExecutor.execute_2D_binning(vis) + # If a column has a Period dtype, or contains Period objects, convert it back to Datetime + if vis.data is not None: + for attr in list(vis.data.columns): + if pd.api.types.is_period_dtype(vis.data.dtypes[attr]) or isinstance( + vis.data[attr].iloc[0], pd.Period + ): + dateColumn = vis.data[attr] + vis.data[attr] = pd.PeriodIndex(dateColumn.values).to_timestamp() + if pd.api.types.is_interval_dtype(vis.data.dtypes[attr]) or isinstance( + vis.data[attr].iloc[0], pd.Interval + ): + vis.data[attr] = vis.data[attr].astype(str) + if vis.mark == "histogram": + chart = Histogram(vis) + elif vis.mark == "bar": + chart = BarChart(vis) + elif vis.mark == "scatter": + chart = ScatterChart(vis) + elif vis.mark == "line": + chart = LineChart(vis) + elif vis.mark == "heatmap": + chart = Heatmap(vis) + else: + chart = None + + if chart: + if vis.plot_config: + chart.chart = vis.plot_config(chart.chart) + if self.output_type == "VegaLite": + chart_dict = chart.chart.to_dict() + # this is a bit of a work around because altair must take a pandas dataframe and we can only generate a luxDataFrame + # chart["data"] = { "values": vis.data.to_dict(orient='records') } + # chart_dict["width"] = 160 + # chart_dict["height"] = 150 + return chart_dict + elif self.output_type == "Altair": + import inspect - if (chart): - if (vis.plot_config): chart.chart = vis.plot_config(chart.chart) - if (self.output_type=="VegaLite"): - chart_dict = chart.chart.to_dict() - # this is a bit of a work around because altair must take a pandas dataframe and we can only generate a luxDataFrame - # chart["data"] = { "values": vis.data.to_dict(orient='records') } - # chart_dict["width"] = 160 - # chart_dict["height"] = 150 - return chart_dict - elif (self.output_type=="Altair"): - import inspect - if (vis.plot_config): chart.code +='\n'.join(inspect.getsource(vis.plot_config).split('\n ')[1:-1]) - chart.code +="\nchart" - chart.code = chart.code.replace('\n\t\t','\n') + if vis.plot_config: + chart.code += "\n".join( + inspect.getsource(vis.plot_config).split("\n ")[1:-1] + ) + chart.code += "\nchart" + chart.code = chart.code.replace("\n\t\t", "\n") - var = vis._source - if var is not None: - all_vars = [] - for f_info in inspect.getouterframes(inspect.currentframe()): - local_vars = f_info.frame.f_back - if local_vars: - callers_local_vars = local_vars.f_locals.items() - possible_vars = [var_name for var_name, var_val in callers_local_vars if var_val is var] - all_vars.extend(possible_vars) - found_variable = [possible_var for possible_var in all_vars if possible_var[0] != '_'][0] - else: # if vis._source was not set when the Vis was created - found_variable = "df" - if standalone: - chart.code = chart.code.replace("placeholder_variable", f"pd.DataFrame({str(vis.data.to_dict())})") - else: - chart.code = chart.code.replace("placeholder_variable", found_variable) # TODO: Placeholder (need to read dynamically via locals()) - return chart.code \ No newline at end of file + var = vis._source + if var is not None: + all_vars = [] + for f_info in inspect.getouterframes(inspect.currentframe()): + local_vars = f_info.frame.f_back + if local_vars: + callers_local_vars = local_vars.f_locals.items() + possible_vars = [ + var_name + for var_name, var_val in callers_local_vars + if var_val is var + ] + all_vars.extend(possible_vars) + found_variable = [ + possible_var + for possible_var in all_vars + if possible_var[0] != "_" + ][0] + else: # if vis._source was not set when the Vis was created + found_variable = "df" + if standalone: + chart.code = chart.code.replace( + "placeholder_variable", + f"pd.DataFrame({str(vis.data.to_dict())})", + ) + else: + chart.code = chart.code.replace( + "placeholder_variable", found_variable + ) # TODO: Placeholder (need to read dynamically via locals()) + return chart.code diff --git a/lux/vislib/altair/BarChart.py b/lux/vislib/altair/BarChart.py index 8cbe9ab7..561a23d4 100644 --- a/lux/vislib/altair/BarChart.py +++ b/lux/vislib/altair/BarChart.py @@ -1,5 +1,5 @@ # 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 @@ -14,66 +14,83 @@ from lux.vislib.altair.AltairChart import AltairChart import altair as alt + alt.data_transformers.disable_max_rows() -from lux.utils.utils import get_agg_title +from lux.utils.utils import get_agg_title + + class BarChart(AltairChart): - """ - BarChart is a subclass of AltairChart that render as a bar charts. - All rendering properties for bar charts are set here. + """ + BarChart is a subclass of AltairChart that render as a bar charts. + All rendering properties for bar charts are set here. + + See Also + -------- + altair-viz.github.io + """ + + def __init__(self, dobj): + super().__init__(dobj) - See Also - -------- - altair-viz.github.io - """ + def __repr__(self): + return f"Bar Chart <{str(self.vis)}>" - def __init__(self,dobj): - super().__init__(dobj) - def __repr__(self): - return f"Bar Chart <{str(self.vis)}>" - def initialize_chart(self): - self.tooltip = False - x_attr = self.vis.get_attr_by_channel("x")[0] - y_attr = self.vis.get_attr_by_channel("y")[0] - - if (x_attr.data_model == "measure"): - agg_title = get_agg_title(x_attr) - measure_attr = x_attr.attribute - bar_attr = y_attr.attribute - y_attr_field = alt.Y(y_attr.attribute, type= y_attr.data_type, axis=alt.Axis(labelOverlap=True)) - x_attr_field = alt.X(x_attr.attribute, type= x_attr.data_type, title=agg_title) - y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', axis=alt.Axis(labelOverlap=True))" - x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', title='{agg_title}')" + def initialize_chart(self): + self.tooltip = False + x_attr = self.vis.get_attr_by_channel("x")[0] + y_attr = self.vis.get_attr_by_channel("y")[0] - if (y_attr.sort=="ascending"): - y_attr_field.sort="-x" - y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', axis=alt.Axis(labelOverlap=True), sort ='-x')" - else: - agg_title = get_agg_title(y_attr) - measure_attr = y_attr.attribute - bar_attr = x_attr.attribute - x_attr_field = alt.X(x_attr.attribute, type = x_attr.data_type,axis=alt.Axis(labelOverlap=True)) - y_attr_field = alt.Y(y_attr.attribute,type=y_attr.data_type,title=agg_title) - x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True))" - y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', title='{agg_title}')" - if (x_attr.sort=="ascending"): - x_attr_field.sort="-y" - x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True),sort='-y')" - k=10 - self._topkcode = "" - n_bars = len(self.data[bar_attr].unique()) - if n_bars>k: # Truncating to only top k - remaining_bars = n_bars-k - self.data = self.data.nlargest(k,measure_attr) - self.text = alt.Chart(self.data).mark_text( - x=155, - y=142, - align="right", - color = "#ff8e04", - fontSize = 11, - text=f"+ {remaining_bars} more ..." - ) + if x_attr.data_model == "measure": + agg_title = get_agg_title(x_attr) + measure_attr = x_attr.attribute + bar_attr = y_attr.attribute + y_attr_field = alt.Y( + y_attr.attribute, + type=y_attr.data_type, + axis=alt.Axis(labelOverlap=True), + ) + x_attr_field = alt.X( + x_attr.attribute, type=x_attr.data_type, title=agg_title + ) + y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', axis=alt.Axis(labelOverlap=True))" + x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', title='{agg_title}')" - self._topkcode = f'''text = alt.Chart(visData).mark_text( + if y_attr.sort == "ascending": + y_attr_field.sort = "-x" + y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', axis=alt.Axis(labelOverlap=True), sort ='-x')" + else: + agg_title = get_agg_title(y_attr) + measure_attr = y_attr.attribute + bar_attr = x_attr.attribute + x_attr_field = alt.X( + x_attr.attribute, + type=x_attr.data_type, + axis=alt.Axis(labelOverlap=True), + ) + y_attr_field = alt.Y( + y_attr.attribute, type=y_attr.data_type, title=agg_title + ) + x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True))" + y_attr_field_code = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', title='{agg_title}')" + if x_attr.sort == "ascending": + x_attr_field.sort = "-y" + x_attr_field_code = f"alt.X('{x_attr.attribute}', type= '{x_attr.data_type}', axis=alt.Axis(labelOverlap=True),sort='-y')" + k = 10 + self._topkcode = "" + n_bars = len(self.data[bar_attr].unique()) + if n_bars > k: # Truncating to only top k + remaining_bars = n_bars - k + self.data = self.data.nlargest(k, measure_attr) + self.text = alt.Chart(self.data).mark_text( + x=155, + y=142, + align="right", + color="#ff8e04", + fontSize=11, + text=f"+ {remaining_bars} more ...", + ) + + self._topkcode = f"""text = alt.Chart(visData).mark_text( x=155, y=142, align="right", @@ -81,33 +98,36 @@ def initialize_chart(self): fontSize = 11, text=f"+ {remaining_bars} more ..." ) - chart = chart + text\n''' - - chart = alt.Chart(self.data).mark_bar().encode( - y = y_attr_field, - x = x_attr_field - ) - # TODO: tooltip messes up the count() bar charts - # Can not do interactive whenever you have default count measure otherwise output strange error (Javascript Error: Cannot read property 'length' of undefined) - #chart = chart.interactive() # If you want to enable Zooming and Panning - - self.code += "import altair as alt\n" - # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" - self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" - self.code += f''' + chart = chart + text\n""" + + chart = alt.Chart(self.data).mark_bar().encode(y=y_attr_field, x=x_attr_field) + # TODO: tooltip messes up the count() bar charts + # Can not do interactive whenever you have default count measure otherwise output strange error (Javascript Error: Cannot read property 'length' of undefined) + # chart = chart.interactive() # If you want to enable Zooming and Panning + + self.code += "import altair as alt\n" + # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" + self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" + self.code += f""" chart = alt.Chart(visData).mark_bar().encode( y = {y_attr_field_code}, x = {x_attr_field_code}, - )\n''' - return chart - - def add_text(self): - if (self._topkcode!=""): - self.chart = self.chart + self.text - self.code += self._topkcode - - def encode_color(self): # override encode_color in AltairChart to enforce add_text occurs afterwards - AltairChart.encode_color(self) - self.add_text() - self.chart = self.chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null - self.code += f'''chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding'))''' \ No newline at end of file + )\n""" + return chart + + def add_text(self): + if self._topkcode != "": + self.chart = self.chart + self.text + self.code += self._topkcode + + def encode_color( + self, + ): # override encode_color in AltairChart to enforce add_text occurs afterwards + AltairChart.encode_color(self) + self.add_text() + self.chart = self.chart.configure_mark( + tooltip=alt.TooltipContent("encoding") + ) # Setting tooltip as non-null + self.code += ( + f"""chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding'))""" + ) diff --git a/lux/vislib/altair/Heatmap.py b/lux/vislib/altair/Heatmap.py index a5d3c106..56ae7276 100644 --- a/lux/vislib/altair/Heatmap.py +++ b/lux/vislib/altair/Heatmap.py @@ -1,5 +1,5 @@ # 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 @@ -13,45 +13,72 @@ # limitations under the License. from lux.vislib.altair.AltairChart import AltairChart -import altair as alt +import altair as alt + alt.data_transformers.disable_max_rows() + + class Heatmap(AltairChart): - """ - Heatmap is a subclass of AltairChart that render as a heatmap. - All rendering properties for heatmap are set here. + """ + Heatmap is a subclass of AltairChart that render as a heatmap. + All rendering properties for heatmap are set here. - See Also - -------- - altair-viz.github.io - """ - def __init__(self,vis): - super().__init__(vis) - def __repr__(self): - return f"Heatmap <{str(self.vis)}>" - def initialize_chart(self): - # return NotImplemented - x_attr = self.vis.get_attr_by_channel("x")[0] - y_attr = self.vis.get_attr_by_channel("y")[0] + See Also + -------- + altair-viz.github.io + """ - chart = alt.Chart(self.data).mark_rect().encode( - x=alt.X('xBinStart', type='quantitative', axis=alt.Axis(title=x_attr.attribute), bin = alt.BinParams(binned=True)), - x2=alt.X2('xBinEnd'), - y=alt.Y('yBinStart', type='quantitative', axis=alt.Axis(title=y_attr.attribute), bin = alt.BinParams(binned=True)), - y2=alt.Y2('yBinEnd'), - opacity = alt.Opacity('count',type='quantitative',scale=alt.Scale(type="log"),legend=None) - ) - chart = chart.configure_scale(minOpacity=0.1,maxOpacity=1) - chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null - chart = chart.interactive() # Enable Zooming and Panning + def __init__(self, vis): + super().__init__(vis) + + def __repr__(self): + return f"Heatmap <{str(self.vis)}>" + + def initialize_chart(self): + # return NotImplemented + x_attr = self.vis.get_attr_by_channel("x")[0] + y_attr = self.vis.get_attr_by_channel("y")[0] + + chart = ( + alt.Chart(self.data) + .mark_rect() + .encode( + x=alt.X( + "xBinStart", + type="quantitative", + axis=alt.Axis(title=x_attr.attribute), + bin=alt.BinParams(binned=True), + ), + x2=alt.X2("xBinEnd"), + y=alt.Y( + "yBinStart", + type="quantitative", + axis=alt.Axis(title=y_attr.attribute), + bin=alt.BinParams(binned=True), + ), + y2=alt.Y2("yBinEnd"), + opacity=alt.Opacity( + "count", + type="quantitative", + scale=alt.Scale(type="log"), + legend=None, + ), + ) + ) + chart = chart.configure_scale(minOpacity=0.1, maxOpacity=1) + chart = chart.configure_mark( + tooltip=alt.TooltipContent("encoding") + ) # Setting tooltip as non-null + chart = chart.interactive() # Enable Zooming and Panning + + #################################### + # Constructing Altair Code String ## + #################################### - #################################### - # Constructing Altair Code String ## - #################################### - - self.code += "import altair as alt\n" - # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" - self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" - self.code += f''' + self.code += "import altair as alt\n" + # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" + self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" + self.code += f""" chart = alt.Chart(visData).mark_rect().encode( x=alt.X('xBinStart', type='quantitative', axis=alt.Axis(title='{x_attr.attribute}'), bin = alt.BinParams(binned=True)), x2=alt.X2('xBinEnd'), @@ -60,5 +87,5 @@ def initialize_chart(self): opacity = alt.Opacity('count',type='quantitative',scale=alt.Scale(type="log"),legend=None) ) chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null - ''' - return chart \ No newline at end of file + """ + return chart diff --git a/lux/vislib/altair/Histogram.py b/lux/vislib/altair/Histogram.py index f6113ba9..b9d1da4a 100644 --- a/lux/vislib/altair/Histogram.py +++ b/lux/vislib/altair/Histogram.py @@ -1,5 +1,5 @@ # 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 @@ -14,61 +14,90 @@ from lux.vislib.altair.AltairChart import AltairChart import altair as alt + alt.data_transformers.disable_max_rows() + + class Histogram(AltairChart): - """ - Histogram is a subclass of AltairChart that render as a histograms. - All rendering properties for histograms are set here. + """ + Histogram is a subclass of AltairChart that render as a histograms. + All rendering properties for histograms are set here. + + See Also + -------- + altair-viz.github.io + """ + + def __init__(self, vis): + super().__init__(vis) + + def __repr__(self): + return f"Histogram <{str(self.vis)}>" + + def initialize_chart(self): + self.tooltip = False + measure = self.vis.get_attr_by_data_model("measure", exclude_record=True)[0] + msr_attr = self.vis.get_attr_by_channel(measure.channel)[0] + x_min = self.vis.min_max[msr_attr.attribute][0] + x_max = self.vis.min_max[msr_attr.attribute][1] - See Also - -------- - altair-viz.github.io - """ - def __init__(self,vis): - super().__init__(vis) - def __repr__(self): - return f"Histogram <{str(self.vis)}>" - def initialize_chart(self): - self.tooltip = False - measure = self.vis.get_attr_by_data_model("measure",exclude_record=True)[0] - msr_attr = self.vis.get_attr_by_channel(measure.channel)[0] - x_min = self.vis.min_max[msr_attr.attribute][0] - x_max = self.vis.min_max[msr_attr.attribute][1] + x_range = abs( + max(self.vis.data[msr_attr.attribute]) + - min(self.vis.data[msr_attr.attribute]) + ) + plot_range = abs(x_max - x_min) + markbar = x_range / plot_range * 12 - x_range = abs(max(self.vis.data[msr_attr.attribute]) - - min(self.vis.data[msr_attr.attribute])) - plot_range = abs(x_max - x_min) - markbar = x_range / plot_range * 12 + if measure.channel == "x": + chart = ( + alt.Chart(self.data) + .mark_bar(size=markbar) + .encode( + alt.X( + msr_attr.attribute, + title=f"{msr_attr.attribute} (binned)", + bin=alt.Bin(binned=True), + type=msr_attr.data_type, + axis=alt.Axis(labelOverlap=True), + scale=alt.Scale(domain=(x_min, x_max)), + ), + alt.Y("Number of Records", type="quantitative"), + ) + ) + elif measure.channel == "y": + chart = ( + alt.Chart(self.data) + .mark_bar(size=markbar) + .encode( + x=alt.X("Number of Records", type="quantitative"), + y=alt.Y( + msr_attr.attribute, + title=f"{msr_attr.attribute} (binned)", + bin=alt.Bin(binned=True), + axis=alt.Axis(labelOverlap=True), + scale=alt.Scale(domain=(x_min, x_max)), + ), + ) + ) + ##################################### + ## Constructing Altair Code String ## + ##################################### - if (measure.channel=="x"): - chart = alt.Chart(self.data).mark_bar(size=markbar).encode( - alt.X(msr_attr.attribute, title=f'{msr_attr.attribute} (binned)',bin=alt.Bin(binned=True), type=msr_attr.data_type, axis=alt.Axis(labelOverlap=True), scale=alt.Scale(domain=(x_min, x_max))), - alt.Y("Number of Records", type="quantitative") - ) - elif (measure.channel=="y"): - chart = alt.Chart(self.data).mark_bar(size=markbar).encode( - x = alt.X("Number of Records", type="quantitative"), - y = alt.Y(msr_attr.attribute, title=f'{msr_attr.attribute} (binned)', bin=alt.Bin(binned=True), axis=alt.Axis(labelOverlap=True), scale=alt.Scale(domain=(x_min, x_max))) - ) - ##################################### - ## Constructing Altair Code String ## - ##################################### - - self.code += "import altair as alt\n" - # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" - self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" - if (measure.channel=="x"): - self.code += f''' + self.code += "import altair as alt\n" + # self.code += f"visData = pd.DataFrame({str(self.data.to_dict(orient='records'))})\n" + self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" + if measure.channel == "x": + self.code += f""" chart = alt.Chart(visData).mark_bar(size={markbar}).encode( alt.X('{msr_attr.attribute}', title='{msr_attr.attribute} (binned)',bin=alt.Bin(binned=True), type='{msr_attr.data_type}', axis=alt.Axis(labelOverlap=True), scale=alt.Scale(domain=({x_min}, {x_max}))), alt.Y("Number of Records", type="quantitative") ) - ''' - elif (measure.channel=="y"): - self.code += f''' + """ + elif measure.channel == "y": + self.code += f""" chart = alt.Chart(visData).mark_bar(size={markbar}).encode( alt.Y('{msr_attr.attribute}', title='{msr_attr.attribute} (binned)',bin=alt.Bin(binned=True), type='{msr_attr.data_type}', axis=alt.Axis(labelOverlap=True), scale=alt.Scale(domain=({x_min}, {x_max}))), alt.X("Number of Records", type="quantitative") ) - ''' - return chart \ No newline at end of file + """ + return chart diff --git a/lux/vislib/altair/LineChart.py b/lux/vislib/altair/LineChart.py index fc94a1e7..1e01eabf 100644 --- a/lux/vislib/altair/LineChart.py +++ b/lux/vislib/altair/LineChart.py @@ -1,5 +1,5 @@ # 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 @@ -14,56 +14,65 @@ from lux.vislib.altair.AltairChart import AltairChart import altair as alt + alt.data_transformers.disable_max_rows() -from lux.utils.utils import get_agg_title +from lux.utils.utils import get_agg_title + + class LineChart(AltairChart): - """ - LineChart is a subclass of AltairChart that render as a line charts. - All rendering properties for line charts are set here. + """ + LineChart is a subclass of AltairChart that render as a line charts. + All rendering properties for line charts are set here. - See Also - -------- - altair-viz.github.io - """ - def __init__(self,dobj): - super().__init__(dobj) - def __repr__(self): - return f"Line Chart <{str(self.vis)}>" - def initialize_chart(self): - self.tooltip = False # tooltip looks weird for line chart - x_attr = self.vis.get_attr_by_channel("x")[0] - y_attr = self.vis.get_attr_by_channel("y")[0] + See Also + -------- + altair-viz.github.io + """ + def __init__(self, dobj): + super().__init__(dobj) - self.code += "import altair as alt\n" - self.code += "import pandas._libs.tslibs.timestamps\n" - self.code += "from pandas._libs.tslibs.timestamps import Timestamp\n" - self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" - - if (y_attr.data_model == "measure"): - agg_title = get_agg_title(y_attr) - x_attr_spec = alt.X(x_attr.attribute, type = x_attr.data_type) - y_attr_spec = alt.Y(y_attr.attribute, type= y_attr.data_type, title=agg_title) - x_attr_field_code = f"alt.X('{x_attr.attribute}', type = '{x_attr.data_type}')" - y_attr_fieldCode = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', title='{agg_title}')" - else: - agg_title = get_agg_title(x_attr) - x_attr_spec = alt.X(x_attr.attribute,type= x_attr.data_type, title=agg_title) - y_attr_spec = alt.Y(y_attr.attribute, type = y_attr.data_type) - x_attr_field_code = f"alt.X('{x_attr.attribute}', type = '{x_attr.data_type}', title='{agg_title}')" - y_attr_fieldCode = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}')" + def __repr__(self): + return f"Line Chart <{str(self.vis)}>" - chart = alt.Chart(self.data).mark_line().encode( - x = x_attr_spec, - y = y_attr_spec - ) - chart = chart.interactive() # Enable Zooming and Panning - self.code += f''' + def initialize_chart(self): + self.tooltip = False # tooltip looks weird for line chart + x_attr = self.vis.get_attr_by_channel("x")[0] + y_attr = self.vis.get_attr_by_channel("y")[0] + + self.code += "import altair as alt\n" + self.code += "import pandas._libs.tslibs.timestamps\n" + self.code += "from pandas._libs.tslibs.timestamps import Timestamp\n" + self.code += f"visData = pd.DataFrame({str(self.data.to_dict())})\n" + + if y_attr.data_model == "measure": + agg_title = get_agg_title(y_attr) + x_attr_spec = alt.X(x_attr.attribute, type=x_attr.data_type) + y_attr_spec = alt.Y( + y_attr.attribute, type=y_attr.data_type, title=agg_title + ) + x_attr_field_code = ( + f"alt.X('{x_attr.attribute}', type = '{x_attr.data_type}')" + ) + y_attr_fieldCode = f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}', title='{agg_title}')" + else: + agg_title = get_agg_title(x_attr) + x_attr_spec = alt.X( + x_attr.attribute, type=x_attr.data_type, title=agg_title + ) + y_attr_spec = alt.Y(y_attr.attribute, type=y_attr.data_type) + x_attr_field_code = f"alt.X('{x_attr.attribute}', type = '{x_attr.data_type}', title='{agg_title}')" + y_attr_fieldCode = ( + f"alt.Y('{y_attr.attribute}', type= '{y_attr.data_type}')" + ) + + chart = alt.Chart(self.data).mark_line().encode(x=x_attr_spec, y=y_attr_spec) + chart = chart.interactive() # Enable Zooming and Panning + self.code += f""" chart = alt.Chart(visData).mark_line().encode( y = {y_attr_fieldCode}, x = {x_attr_field_code}, ) chart = chart.interactive() # Enable Zooming and Panning - ''' - return chart - \ No newline at end of file + """ + return chart diff --git a/lux/vislib/altair/ScatterChart.py b/lux/vislib/altair/ScatterChart.py index d9370b23..a6463041 100644 --- a/lux/vislib/altair/ScatterChart.py +++ b/lux/vislib/altair/ScatterChart.py @@ -1,5 +1,5 @@ # 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 @@ -13,49 +13,69 @@ # limitations under the License. from lux.vislib.altair.AltairChart import AltairChart -import altair as alt +import altair as alt + alt.data_transformers.disable_max_rows() + + class ScatterChart(AltairChart): - """ - ScatterChart is a subclass of AltairChart that render as a scatter charts. - All rendering properties for scatter charts are set here. - - See Also - -------- - altair-viz.github.io - """ - def __init__(self,vis): - super().__init__(vis) - def __repr__(self): - return f"ScatterChart <{str(self.vis)}>" - def initialize_chart(self): - x_attr = self.vis.get_attr_by_channel("x")[0] - y_attr = self.vis.get_attr_by_channel("y")[0] - x_min = self.vis.min_max[x_attr.attribute][0] - x_max = self.vis.min_max[x_attr.attribute][1] - - y_min = self.vis.min_max[y_attr.attribute][0] - y_max = self.vis.min_max[y_attr.attribute][1] - - chart = alt.Chart(self.data).mark_circle().encode( - x=alt.X(x_attr.attribute,scale=alt.Scale(domain=(x_min, x_max)),type=x_attr.data_type), - y=alt.Y(y_attr.attribute,scale=alt.Scale(domain=(y_min, y_max)),type=y_attr.data_type) - ) - chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null - chart = chart.interactive() # Enable Zooming and Panning + """ + ScatterChart is a subclass of AltairChart that render as a scatter charts. + All rendering properties for scatter charts are set here. + + See Also + -------- + altair-viz.github.io + """ + + def __init__(self, vis): + super().__init__(vis) + + def __repr__(self): + return f"ScatterChart <{str(self.vis)}>" + + def initialize_chart(self): + x_attr = self.vis.get_attr_by_channel("x")[0] + y_attr = self.vis.get_attr_by_channel("y")[0] + x_min = self.vis.min_max[x_attr.attribute][0] + x_max = self.vis.min_max[x_attr.attribute][1] + + y_min = self.vis.min_max[y_attr.attribute][0] + y_max = self.vis.min_max[y_attr.attribute][1] + + chart = ( + alt.Chart(self.data) + .mark_circle() + .encode( + x=alt.X( + x_attr.attribute, + scale=alt.Scale(domain=(x_min, x_max)), + type=x_attr.data_type, + ), + y=alt.Y( + y_attr.attribute, + scale=alt.Scale(domain=(y_min, y_max)), + type=y_attr.data_type, + ), + ) + ) + chart = chart.configure_mark( + tooltip=alt.TooltipContent("encoding") + ) # Setting tooltip as non-null + chart = chart.interactive() # Enable Zooming and Panning + + ##################################### + ## Constructing Altair Code String ## + ##################################### - ##################################### - ## Constructing Altair Code String ## - ##################################### - - self.code += "import altair as alt\n" - dfname = "placeholder_variable" - self.code += f''' + self.code += "import altair as alt\n" + dfname = "placeholder_variable" + self.code += f""" chart = alt.Chart({dfname}).mark_circle().encode( x=alt.X('{x_attr.attribute}',scale=alt.Scale(domain=({x_min}, {x_max})),type='{x_attr.data_type}'), y=alt.Y('{y_attr.attribute}',scale=alt.Scale(domain=({y_min}, {y_max})),type='{y_attr.data_type}') ) chart = chart.configure_mark(tooltip=alt.TooltipContent('encoding')) # Setting tooltip as non-null chart = chart.interactive() # Enable Zooming and Panning - ''' - return chart \ No newline at end of file + """ + return chart diff --git a/lux/vislib/altair/__init__.py b/lux/vislib/altair/__init__.py index cbfa9f5b..948becf5 100644 --- a/lux/vislib/altair/__init__.py +++ b/lux/vislib/altair/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -11,4 +11,3 @@ # 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. - diff --git a/requirements-dev.txt b/requirements-dev.txt index 3d919655..0365d5bd 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,3 +2,4 @@ pytest>=5.3.1 pytest-cov>=2.8.1 Sphinx>=3.0.2 sphinx-rtd-theme>=0.4.3 +black diff --git a/requirements.txt b/requirements.txt index 0b31c7a4..b23c6009 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,7 @@ scipy>=1.3.3 altair>=4.0.0 pandas>=1.1.0 scikit-learn>=0.22 -lux-widget>=0.1.0 \ No newline at end of file +# Install only to use SQLExecutor +# psycopg2>=2.8.5 +# psycopg2-binary>=2.8.5 +lux-widget>=0.1.0 diff --git a/setup.py b/setup.py index 43b731a6..bca149b8 100644 --- a/setup.py +++ b/setup.py @@ -4,43 +4,41 @@ HERE = path.abspath(path.dirname(__file__)) # Get the long description from the README file -with open(path.join(HERE, 'README.md'), encoding='utf-8') as f: +with open(path.join(HERE, "README.md"), encoding="utf-8") as f: long_description = f.read() -with open(path.join(HERE, 'requirements.txt')) as fp: +with open(path.join(HERE, "requirements.txt")) as fp: install_requires = fp.read() version_dict = {} -with open(path.join(HERE, 'lux/_version.py')) as fp: +with open(path.join(HERE, "lux/_version.py")) as fp: exec(fp.read(), {}, version_dict) version = version_dict["__version__"] setup( - name='lux-api', # PyPI Name (pip install [name]) + name="lux-api", # PyPI Name (pip install [name]) version=version, # Required - description='A Python API for Intelligent Data Discovery', - long_description=long_description, - long_description_content_type='text/markdown', - url='https://github.com/lux-org/lux', - author='Doris Jung-Lin Lee', - author_email='dorisjunglinlee@gmail.com', - license = 'Apache-2.0 License', - classifiers=[ - 'Development Status :: 1 - Planning', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'Intended Audience :: Other Audience', - 'Topic :: Scientific/Engineering :: Information Analysis', - 'Topic :: Scientific/Engineering :: Visualization', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python :: 3' + description="A Python API for Intelligent Data Discovery", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/lux-org/lux", + author="Doris Jung-Lin Lee", + author_email="dorisjunglinlee@gmail.com", + license="Apache-2.0 License", + classifiers=[ + "Development Status :: 1 - Planning", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Intended Audience :: Other Audience", + "Topic :: Scientific/Engineering :: Information Analysis", + "Topic :: Scientific/Engineering :: Visualization", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", ], - keywords= ['Visualization','Analytics','Data Science','Data Analysis'], + keywords=["Visualization", "Analytics", "Data Science", "Data Analysis"], include_data_package=True, packages=find_packages(), # Required - python_requires='>=3.5', + python_requires=">=3.5", install_requires=install_requires, - extras_require={ - 'test': ['pytest'] - } -) \ No newline at end of file + extras_require={"test": ["pytest"]}, +) diff --git a/tests/__init__.py b/tests/__init__.py index cbfa9f5b..948becf5 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,5 @@ # 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 @@ -11,4 +11,3 @@ # 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. - diff --git a/tests/context.py b/tests/context.py index 00894665..42b3eb15 100644 --- a/tests/context.py +++ b/tests/context.py @@ -1,5 +1,5 @@ # 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 @@ -14,6 +14,7 @@ import os import sys -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -import lux \ No newline at end of file +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) + +import lux diff --git a/tests/test_action.py b/tests/test_action.py index 2b80a82a..3b3097ad 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -1,5 +1,5 @@ # 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 @@ -17,92 +17,183 @@ import pandas as pd from lux.vis.Vis import Vis + + def test_vary_filter_val(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true' - df = pd.read_csv(url) - vis = Vis(["Height","SportType=Ball"],df) - df.set_intent_as_vis(vis) - df._repr_html_() - assert len(df.recommendation["Filter"]) == len(df["SportType"].unique())-1 + url = ( + "https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true" + ) + df = pd.read_csv(url) + vis = Vis(["Height", "SportType=Ball"], df) + df.set_intent_as_vis(vis) + df._repr_html_() + assert len(df.recommendation["Filter"]) == len(df["SportType"].unique()) - 1 + + def test_filter_inequality(): - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + + df.set_intent( + [ + lux.Clause(attribute="Horsepower"), + lux.Clause(attribute="MilesPerGal"), + lux.Clause(attribute="Acceleration", filter_op=">", value=10), + ] + ) + df._repr_html_() + + from lux.utils.utils import get_filter_specs + + complement_vis = df.recommendation["Filter"][0] + fltr_clause = get_filter_specs(complement_vis._intent)[0] + assert fltr_clause.filter_op == "<=" + assert fltr_clause.value == 10 - df.set_intent([lux.Clause(attribute = "Horsepower"),lux.Clause(attribute = "MilesPerGal"),lux.Clause(attribute = "Acceleration", filter_op=">",value = 10)]) - df._repr_html_() - from lux.utils.utils import get_filter_specs - complement_vis = df.recommendation["Filter"][0] - fltr_clause = get_filter_specs(complement_vis._intent)[0] - assert fltr_clause.filter_op =="<=" - assert fltr_clause.value ==10 def test_generalize_action(): - #test that generalize action creates all unique visualizations - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - df.set_intent(["Acceleration", "MilesPerGal", "Cylinders", "Origin=USA"]) - df._repr_html_() - assert(len(df.recommendation['Generalize']) == 4) - v1 = df.recommendation['Generalize'][0] - v2 = df.recommendation['Generalize'][1] - v3 = df.recommendation['Generalize'][2] - v4 = df.recommendation['Generalize'][3] - - for clause in v4._inferred_intent: - assert clause.value=="" #No filter value - assert v4.title =='Overall' - - check1 = v1 != v2 and v1 != v3 and v1 != v4 - check2 = v2 != v3 and v2 != v4 - check3 = v3 != v4 - assert(check1 and check2 and check3) + # test that generalize action creates all unique visualizations + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + df.set_intent(["Acceleration", "MilesPerGal", "Cylinders", "Origin=USA"]) + df._repr_html_() + assert len(df.recommendation["Generalize"]) == 4 + v1 = df.recommendation["Generalize"][0] + v2 = df.recommendation["Generalize"][1] + v3 = df.recommendation["Generalize"][2] + v4 = df.recommendation["Generalize"][3] + + for clause in v4._inferred_intent: + assert clause.value == "" # No filter value + assert v4.title == "Overall" + + check1 = v1 != v2 and v1 != v3 and v1 != v4 + check2 = v2 != v3 and v2 != v4 + check3 = v3 != v4 + assert check1 and check2 and check3 + def test_row_column_group(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/state_timeseries.csv?raw=true' - df = pd.read_csv(url) - df["Date"] = pd.to_datetime(df["Date"]) - tseries = df.pivot(index="State",columns="Date",values="Value") - # Interpolating missing values - tseries[tseries.columns.min()] = tseries[tseries.columns.min()].fillna(0) - tseries[tseries.columns.max()] = tseries[tseries.columns.max()].fillna(tseries.max(axis=1)) - tseries = tseries.interpolate('zero',axis=1) - tseries._repr_html_() - assert list(tseries.recommendation.keys() ) == ['Row Groups','Column Groups'] + url = "https://github.com/lux-org/lux-datasets/blob/master/data/state_timeseries.csv?raw=true" + df = pd.read_csv(url) + df["Date"] = pd.to_datetime(df["Date"]) + tseries = df.pivot(index="State", columns="Date", values="Value") + # Interpolating missing values + tseries[tseries.columns.min()] = tseries[tseries.columns.min()].fillna(0) + tseries[tseries.columns.max()] = tseries[tseries.columns.max()].fillna( + tseries.max(axis=1) + ) + tseries = tseries.interpolate("zero", axis=1) + tseries._repr_html_() + assert list(tseries.recommendation.keys()) == ["Row Groups", "Column Groups"] + def test_groupby(): - df = pd.read_csv("lux/data/college.csv") - groupbyResult = df.groupby("Region").sum() - groupbyResult._repr_html_() - assert list(groupbyResult.recommendation.keys() ) == ['Column Groups'] + df = pd.read_csv("lux/data/college.csv") + groupbyResult = df.groupby("Region").sum() + groupbyResult._repr_html_() + assert list(groupbyResult.recommendation.keys()) == ["Column Groups"] + def test_crosstab(): - # Example from http://www.datasciencemadesimple.com/cross-tab-cross-table-python-pandas/ - d = { - 'Name':['Alisa','Bobby','Cathrine','Alisa','Bobby','Cathrine', - 'Alisa','Bobby','Cathrine','Alisa','Bobby','Cathrine'], - 'Exam':['Semester 1','Semester 1','Semester 1','Semester 1','Semester 1','Semester 1', - 'Semester 2','Semester 2','Semester 2','Semester 2','Semester 2','Semester 2'], - - 'Subject':['Mathematics','Mathematics','Mathematics','Science','Science','Science', - 'Mathematics','Mathematics','Mathematics','Science','Science','Science'], - 'Result':['Pass','Pass','Fail','Pass','Fail','Pass','Pass','Fail','Fail','Pass','Pass','Fail']} - - df = pd.DataFrame(d,columns=['Name','Exam','Subject','Result']) - result = pd.crosstab([df.Exam],df.Result) - result._repr_html_() - assert list(result.recommendation.keys() ) == ['Row Groups','Column Groups'] + # Example from http://www.datasciencemadesimple.com/cross-tab-cross-table-python-pandas/ + d = { + "Name": [ + "Alisa", + "Bobby", + "Cathrine", + "Alisa", + "Bobby", + "Cathrine", + "Alisa", + "Bobby", + "Cathrine", + "Alisa", + "Bobby", + "Cathrine", + ], + "Exam": [ + "Semester 1", + "Semester 1", + "Semester 1", + "Semester 1", + "Semester 1", + "Semester 1", + "Semester 2", + "Semester 2", + "Semester 2", + "Semester 2", + "Semester 2", + "Semester 2", + ], + "Subject": [ + "Mathematics", + "Mathematics", + "Mathematics", + "Science", + "Science", + "Science", + "Mathematics", + "Mathematics", + "Mathematics", + "Science", + "Science", + "Science", + ], + "Result": [ + "Pass", + "Pass", + "Fail", + "Pass", + "Fail", + "Pass", + "Pass", + "Fail", + "Fail", + "Pass", + "Pass", + "Fail", + ], + } + + df = pd.DataFrame(d, columns=["Name", "Exam", "Subject", "Result"]) + result = pd.crosstab([df.Exam], df.Result) + result._repr_html_() + assert list(result.recommendation.keys()) == ["Row Groups", "Column Groups"] + def test_custom_aggregation(): - import numpy as np - df = pd.read_csv("lux/data/college.csv") - df.set_intent(["HighestDegree",lux.Clause("AverageCost",aggregation=np.ptp)]) - df._repr_html_() - assert list(df.recommendation.keys()) ==['Enhance', 'Filter', 'Generalize'] + import numpy as np + + df = pd.read_csv("lux/data/college.csv") + df.set_intent(["HighestDegree", lux.Clause("AverageCost", aggregation=np.ptp)]) + df._repr_html_() + assert list(df.recommendation.keys()) == ["Enhance", "Filter", "Generalize"] + + def test_year_filter_value(): - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - df.set_intent(["Acceleration","Horsepower"]) - df._repr_html_() - list_of_vis_with_year_filter = list(filter(lambda vis: len(list(filter(lambda clause: clause.value!='' and clause.attribute=="Year",vis._intent)))!=0, df.recommendation["Filter"])) - vis = list_of_vis_with_year_filter[0] - assert "T00:00:00.000000000" not in vis.to_Altair(), "Year filter title contains extraneous string, not displayed as summarized string" + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + df.set_intent(["Acceleration", "Horsepower"]) + df._repr_html_() + list_of_vis_with_year_filter = list( + filter( + lambda vis: len( + list( + filter( + lambda clause: clause.value != "" + and clause.attribute == "Year", + vis._intent, + ) + ) + ) + != 0, + df.recommendation["Filter"], + ) + ) + vis = list_of_vis_with_year_filter[0] + assert ( + "T00:00:00.000000000" not in vis.to_Altair() + ), "Year filter title contains extraneous string, not displayed as summarized string" diff --git a/tests/test_compiler.py b/tests/test_compiler.py index cda948d1..be3c32e8 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -1,5 +1,5 @@ # 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 @@ -18,27 +18,31 @@ from lux.vis.Vis import Vis from lux.vis.VisList import VisList + def test_underspecified_no_vis(test_recs): - no_vis_actions = ["Correlation", "Distribution", "Occurrence","Temporal"] - df = pd.read_csv("lux/data/car.csv") - test_recs(df, no_vis_actions) - assert len(df.current_vis) == 0 + no_vis_actions = ["Correlation", "Distribution", "Occurrence", "Temporal"] + df = pd.read_csv("lux/data/car.csv") + test_recs(df, no_vis_actions) + assert len(df.current_vis) == 0 + + # test only one filter context case. + df.set_intent([lux.Clause(attribute="Origin", filter_op="=", value="USA")]) + test_recs(df, no_vis_actions) + assert len(df.current_vis) == 0 - # test only one filter context case. - df.set_intent([lux.Clause(attribute ="Origin", filter_op="=", value="USA")]) - test_recs(df, no_vis_actions) - assert len(df.current_vis) == 0 def test_underspecified_single_vis(test_recs): - one_vis_actions = ["Enhance", "Filter", "Generalize"] - df = pd.read_csv("lux/data/car.csv") - df.set_intent([lux.Clause(attribute ="MilesPerGal"), lux.Clause(attribute ="Weight")]) - test_recs(df, one_vis_actions) - assert len(df.current_vis) == 1 - assert df.current_vis[0].mark == "scatter" - for attr in df.current_vis[0]._inferred_intent: assert attr.data_model == "measure" - for attr in df.current_vis[0]._inferred_intent: assert attr.data_type == "quantitative" - + one_vis_actions = ["Enhance", "Filter", "Generalize"] + df = pd.read_csv("lux/data/car.csv") + df.set_intent([lux.Clause(attribute="MilesPerGal"), lux.Clause(attribute="Weight")]) + test_recs(df, one_vis_actions) + assert len(df.current_vis) == 1 + assert df.current_vis[0].mark == "scatter" + for attr in df.current_vis[0]._inferred_intent: + assert attr.data_model == "measure" + for attr in df.current_vis[0]._inferred_intent: + assert attr.data_type == "quantitative" + # def test_underspecified_vis_collection(test_recs): # multiple_vis_actions = ["Current viss"] @@ -72,182 +76,316 @@ def test_underspecified_single_vis(test_recs): # assert len(df.current_vis) == len([vis.get_attr_by_data_model("measure") for vis in df.current_vis]) #should be 25 # test_recs(df, multiple_vis_actions) def test_set_intent_as_vis(test_recs): - df = pd.read_csv("lux/data/car.csv") - df._repr_html_() - vis = df.recommendation["Correlation"][0] - df.intent = vis - df._repr_html_() - test_recs(df,["Enhance","Filter","Generalize"]) + df = pd.read_csv("lux/data/car.csv") + df._repr_html_() + vis = df.recommendation["Correlation"][0] + df.intent = vis + df._repr_html_() + test_recs(df, ["Enhance", "Filter", "Generalize"]) + @pytest.fixture def test_recs(): - def test_recs_function(df, actions): - df._repr_html_() - assert (len(df.recommendation) > 0) - recKeys = list(df.recommendation.keys()) - list_equal(recKeys,actions) - return test_recs_function + def test_recs_function(df, actions): + df._repr_html_() + assert len(df.recommendation) > 0 + recKeys = list(df.recommendation.keys()) + list_equal(recKeys, actions) + + return test_recs_function + def test_parse(): - df = pd.read_csv("lux/data/car.csv") - vlst = VisList([lux.Clause("Origin=?"), lux.Clause(attribute ="MilesPerGal")],df) - assert len(vlst) == 3 + df = pd.read_csv("lux/data/car.csv") + vlst = VisList([lux.Clause("Origin=?"), lux.Clause(attribute="MilesPerGal")], df) + assert len(vlst) == 3 + + df = pd.read_csv("lux/data/car.csv") + vlst = VisList([lux.Clause("Origin=?"), lux.Clause("MilesPerGal")], df) + assert len(vlst) == 3 + - df = pd.read_csv("lux/data/car.csv") - vlst = VisList([lux.Clause("Origin=?"), lux.Clause("MilesPerGal")],df) - assert len(vlst) == 3 def test_underspecified_vis_collection_zval(): - # check if the number of charts is correct - df = pd.read_csv("lux/data/car.csv") - vlst = VisList([lux.Clause(attribute ="Origin", filter_op="=", value="?"), lux.Clause(attribute ="MilesPerGal")],df) - assert len(vlst) == 3 + # check if the number of charts is correct + df = pd.read_csv("lux/data/car.csv") + vlst = VisList( + [ + lux.Clause(attribute="Origin", filter_op="=", value="?"), + lux.Clause(attribute="MilesPerGal"), + ], + df, + ) + assert len(vlst) == 3 + + # does not work + # df = pd.read_csv("lux/data/car.csv") + # vlst = VisList([lux.Clause(attribute = ["Origin","Cylinders"], filter_op="=",value="?"),lux.Clause(attribute = ["Horsepower"]),lux.Clause(attribute = "Weight")],df) + # assert len(vlst) == 8 - #does not work - # df = pd.read_csv("lux/data/car.csv") - # vlst = VisList([lux.Clause(attribute = ["Origin","Cylinders"], filter_op="=",value="?"),lux.Clause(attribute = ["Horsepower"]),lux.Clause(attribute = "Weight")],df) - # assert len(vlst) == 8 def test_sort_bar(): - from lux.processor.Compiler import Compiler - from lux.vis.Vis import Vis - df = pd.read_csv("lux/data/car.csv") - vis = Vis([lux.Clause(attribute="Acceleration",data_model="measure",data_type="quantitative"), - lux.Clause(attribute="Origin",data_model="dimension",data_type="nominal")],df) - assert vis.mark == "bar" - assert vis._inferred_intent[1].sort == '' - - df = pd.read_csv("lux/data/car.csv") - vis = Vis([lux.Clause(attribute="Acceleration",data_model="measure",data_type="quantitative"), - lux.Clause(attribute="Name",data_model="dimension",data_type="nominal")],df) - assert vis.mark == "bar" - assert vis._inferred_intent[1].sort == 'ascending' + from lux.processor.Compiler import Compiler + from lux.vis.Vis import Vis + + df = pd.read_csv("lux/data/car.csv") + vis = Vis( + [ + lux.Clause( + attribute="Acceleration", data_model="measure", data_type="quantitative" + ), + lux.Clause(attribute="Origin", data_model="dimension", data_type="nominal"), + ], + df, + ) + assert vis.mark == "bar" + assert vis._inferred_intent[1].sort == "" + + df = pd.read_csv("lux/data/car.csv") + vis = Vis( + [ + lux.Clause( + attribute="Acceleration", data_model="measure", data_type="quantitative" + ), + lux.Clause(attribute="Name", data_model="dimension", data_type="nominal"), + ], + df, + ) + assert vis.mark == "bar" + assert vis._inferred_intent[1].sort == "ascending" + def test_specified_vis_collection(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' - df = pd.read_csv(url) - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype + url = "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" + df = pd.read_csv(url) + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + + vlst = VisList( + [ + lux.Clause(attribute="Horsepower"), + lux.Clause(attribute="Brand"), + lux.Clause(attribute="Origin", value=["Japan", "USA"]), + ], + df, + ) + assert len(vlst) == 2 + + vlst = VisList( + [ + lux.Clause(attribute=["Horsepower", "Weight"]), + lux.Clause(attribute="Brand"), + lux.Clause(attribute="Origin", value=["Japan", "USA"]), + ], + df, + ) + assert len(vlst) == 4 + + # test if z axis has been filtered correctly + chart_titles = [vis.title for vis in vlst] + assert "Origin = USA" and "Origin = Japan" in chart_titles + assert "Origin = Europe" not in chart_titles - vlst = VisList([lux.Clause(attribute="Horsepower"),lux.Clause(attribute="Brand"), lux.Clause(attribute = "Origin",value=["Japan","USA"])],df) - assert len(vlst) == 2 - vlst = VisList([lux.Clause(attribute=["Horsepower","Weight"]),lux.Clause(attribute="Brand"), lux.Clause(attribute = "Origin",value=["Japan","USA"])],df) - assert len(vlst) == 4 +def test_specified_channel_enforced_vis_collection(): + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + visList = VisList( + [lux.Clause(attribute="?"), lux.Clause(attribute="MilesPerGal", channel="x")], + df, + ) + for vis in visList: + check_attribute_on_channel(vis, "MilesPerGal", "x") - # test if z axis has been filtered correctly - chart_titles = [vis.title for vis in vlst] - assert "Origin = USA" and "Origin = Japan" in chart_titles - assert "Origin = Europe" not in chart_titles +def test_autoencoding_scatter(): + # No channel specified + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + vis = Vis([lux.Clause(attribute="MilesPerGal"), lux.Clause(attribute="Weight")], df) + check_attribute_on_channel(vis, "MilesPerGal", "x") + check_attribute_on_channel(vis, "Weight", "y") + + # Partial channel specified + vis = Vis( + [ + lux.Clause(attribute="MilesPerGal", channel="y"), + lux.Clause(attribute="Weight"), + ], + df, + ) + check_attribute_on_channel(vis, "MilesPerGal", "y") + check_attribute_on_channel(vis, "Weight", "x") + + # Full channel specified + vis = Vis( + [ + lux.Clause(attribute="MilesPerGal", channel="y"), + lux.Clause(attribute="Weight", channel="x"), + ], + df, + ) + check_attribute_on_channel(vis, "MilesPerGal", "y") + check_attribute_on_channel(vis, "Weight", "x") + # Duplicate channel specified + with pytest.raises(ValueError): + # Should throw error because there should not be columns with the same channel specified + df.set_intent( + [ + lux.Clause(attribute="MilesPerGal", channel="x"), + lux.Clause(attribute="Weight", channel="x"), + ] + ) -def test_specified_channel_enforced_vis_collection(): - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - visList = VisList([lux.Clause(attribute="?"),lux.Clause(attribute="MilesPerGal",channel="x")],df) - for vis in visList: - check_attribute_on_channel(vis, "MilesPerGal", "x") -def test_autoencoding_scatter(): - # No channel specified - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - vis = Vis([lux.Clause(attribute="MilesPerGal"), lux.Clause(attribute="Weight")],df) - check_attribute_on_channel(vis, "MilesPerGal", "x") - check_attribute_on_channel(vis, "Weight", "y") - - # Partial channel specified - vis = Vis([lux.Clause(attribute="MilesPerGal", channel="y"), lux.Clause(attribute="Weight")],df) - check_attribute_on_channel(vis, "MilesPerGal", "y") - check_attribute_on_channel(vis, "Weight", "x") - - # Full channel specified - vis = Vis([lux.Clause(attribute="MilesPerGal", channel="y"), lux.Clause(attribute="Weight", channel="x")],df) - check_attribute_on_channel(vis, "MilesPerGal", "y") - check_attribute_on_channel(vis, "Weight", "x") - # Duplicate channel specified - with pytest.raises(ValueError): - # Should throw error because there should not be columns with the same channel specified - df.set_intent([lux.Clause(attribute="MilesPerGal", channel="x"), lux.Clause(attribute="Weight", channel="x")]) - def test_autoencoding_histogram(): - # No channel specified - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - vis = Vis([lux.Clause(attribute="MilesPerGal", channel="y")],df) - check_attribute_on_channel(vis, "MilesPerGal", "y") + # No channel specified + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + vis = Vis([lux.Clause(attribute="MilesPerGal", channel="y")], df) + check_attribute_on_channel(vis, "MilesPerGal", "y") + + vis = Vis([lux.Clause(attribute="MilesPerGal", channel="x")], df) + assert vis.get_attr_by_channel("x")[0].attribute == "MilesPerGal" + assert vis.get_attr_by_channel("y")[0].attribute == "Record" - vis = Vis([lux.Clause(attribute="MilesPerGal",channel="x")],df) - assert vis.get_attr_by_channel("x")[0].attribute == "MilesPerGal" - assert vis.get_attr_by_channel("y")[0].attribute == "Record" def test_autoencoding_line_chart(): - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - vis = Vis([lux.Clause(attribute="Year"), lux.Clause(attribute="Acceleration")],df) - check_attribute_on_channel(vis, "Year", "x") - check_attribute_on_channel(vis, "Acceleration", "y") - - # Partial channel specified - vis = Vis([lux.Clause(attribute="Year", channel="y"), lux.Clause(attribute="Acceleration")],df) - check_attribute_on_channel(vis, "Year", "y") - check_attribute_on_channel(vis, "Acceleration", "x") - - # Full channel specified - vis = Vis([lux.Clause(attribute="Year", channel="y"), lux.Clause(attribute="Acceleration", channel="x")],df) - check_attribute_on_channel(vis, "Year", "y") - check_attribute_on_channel(vis, "Acceleration", "x") - - with pytest.raises(ValueError): - # Should throw error because there should not be columns with the same channel specified - df.set_intent([lux.Clause(attribute="Year", channel="x"), lux.Clause(attribute="Acceleration", channel="x")]) + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + vis = Vis([lux.Clause(attribute="Year"), lux.Clause(attribute="Acceleration")], df) + check_attribute_on_channel(vis, "Year", "x") + check_attribute_on_channel(vis, "Acceleration", "y") + + # Partial channel specified + vis = Vis( + [ + lux.Clause(attribute="Year", channel="y"), + lux.Clause(attribute="Acceleration"), + ], + df, + ) + check_attribute_on_channel(vis, "Year", "y") + check_attribute_on_channel(vis, "Acceleration", "x") + + # Full channel specified + vis = Vis( + [ + lux.Clause(attribute="Year", channel="y"), + lux.Clause(attribute="Acceleration", channel="x"), + ], + df, + ) + check_attribute_on_channel(vis, "Year", "y") + check_attribute_on_channel(vis, "Acceleration", "x") + + with pytest.raises(ValueError): + # Should throw error because there should not be columns with the same channel specified + df.set_intent( + [ + lux.Clause(attribute="Year", channel="x"), + lux.Clause(attribute="Acceleration", channel="x"), + ] + ) + def test_autoencoding_color_line_chart(): - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - intent = [lux.Clause(attribute="Year"), lux.Clause(attribute="Acceleration"), lux.Clause(attribute="Origin")] - vis = Vis(intent,df) - check_attribute_on_channel(vis, "Year", "x") - check_attribute_on_channel(vis, "Acceleration", "y") - check_attribute_on_channel(vis, "Origin", "color") + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + intent = [ + lux.Clause(attribute="Year"), + lux.Clause(attribute="Acceleration"), + lux.Clause(attribute="Origin"), + ] + vis = Vis(intent, df) + check_attribute_on_channel(vis, "Year", "x") + check_attribute_on_channel(vis, "Acceleration", "y") + check_attribute_on_channel(vis, "Origin", "color") + def test_autoencoding_color_scatter_chart(): - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - vis = Vis([lux.Clause(attribute="Horsepower"), lux.Clause(attribute="Acceleration"), lux.Clause(attribute="Origin")],df) - check_attribute_on_channel(vis, "Origin", "color") + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + vis = Vis( + [ + lux.Clause(attribute="Horsepower"), + lux.Clause(attribute="Acceleration"), + lux.Clause(attribute="Origin"), + ], + df, + ) + check_attribute_on_channel(vis, "Origin", "color") + + vis = Vis( + [ + lux.Clause(attribute="Horsepower"), + lux.Clause(attribute="Acceleration", channel="color"), + lux.Clause(attribute="Origin"), + ], + df, + ) + check_attribute_on_channel(vis, "Acceleration", "color") - vis = Vis([lux.Clause(attribute="Horsepower"), lux.Clause(attribute="Acceleration", channel="color"), lux.Clause(attribute="Origin")],df) - check_attribute_on_channel(vis, "Acceleration", "color") def test_populate_options(): - from lux.processor.Compiler import Compiler - df = pd.read_csv("lux/data/car.csv") - df.set_intent([lux.Clause(attribute="?"), lux.Clause(attribute="MilesPerGal")]) - col_set = set() - for specOptions in Compiler.populate_wildcard_options(df._intent, df)["attributes"]: - for clause in specOptions: - col_set.add(clause.attribute) - assert list_equal(list(col_set), list(df.columns)) - - df.set_intent([lux.Clause(attribute="?", data_model="measure"), lux.Clause(attribute="MilesPerGal")]) - df._repr_html_() - col_set = set() - for specOptions in Compiler.populate_wildcard_options(df._intent, df)["attributes"]: - for clause in specOptions: - col_set.add(clause.attribute) - assert list_equal(list(col_set), ['Acceleration', 'Weight', 'Horsepower', 'MilesPerGal', 'Displacement']) + from lux.processor.Compiler import Compiler + + df = pd.read_csv("lux/data/car.csv") + df.set_intent([lux.Clause(attribute="?"), lux.Clause(attribute="MilesPerGal")]) + col_set = set() + for specOptions in Compiler.populate_wildcard_options(df._intent, df)["attributes"]: + for clause in specOptions: + col_set.add(clause.attribute) + assert list_equal(list(col_set), list(df.columns)) + + df.set_intent( + [ + lux.Clause(attribute="?", data_model="measure"), + lux.Clause(attribute="MilesPerGal"), + ] + ) + df._repr_html_() + col_set = set() + for specOptions in Compiler.populate_wildcard_options(df._intent, df)["attributes"]: + for clause in specOptions: + col_set.add(clause.attribute) + assert list_equal( + list(col_set), + ["Acceleration", "Weight", "Horsepower", "MilesPerGal", "Displacement"], + ) + def test_remove_all_invalid(): - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - # with pytest.warns(UserWarning,match="duplicate attribute specified in the intent"): - df.set_intent([lux.Clause(attribute = "Origin", filter_op="=",value="USA"),lux.Clause(attribute = "Origin")]) - df._repr_html_() - assert len(df.current_vis)==0 + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + # with pytest.warns(UserWarning,match="duplicate attribute specified in the intent"): + df.set_intent( + [ + lux.Clause(attribute="Origin", filter_op="=", value="USA"), + lux.Clause(attribute="Origin"), + ] + ) + df._repr_html_() + assert len(df.current_vis) == 0 + def list_equal(l1, l2): l1.sort() l2.sort() - return l1==l2 + return l1 == l2 + def check_attribute_on_channel(vis, attr_name, channelName): - assert vis.get_attr_by_channel(channelName)[0].attribute == attr_name + assert vis.get_attr_by_channel(channelName)[0].attribute == attr_name diff --git a/tests/test_config.py b/tests/test_config.py index 3b9be963..adfd2655 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,5 +1,5 @@ # 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 @@ -19,130 +19,168 @@ from lux.vis.VisList import VisList import lux -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 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) - assert(len(df.recommendation["Distribution"]) > 0) + 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("Occurrence" in df.recommendation) - assert(len(df.recommendation["Occurrence"]) > 0) + assert "Temporal" in df.recommendation + assert len(df.recommendation["Temporal"]) > 0 - assert("Temporal" in df.recommendation) - assert(len(df.recommendation["Temporal"]) > 0) + assert "Correlation" in df.recommendation + assert len(df.recommendation["Correlation"]) > 0 - assert("Correlation" in df.recommendation) - assert(len(df.recommendation["Correlation"]) > 0) def test_fail_validator(): - df = register_new_action() - df._repr_html_() - assert("bars" not in df.recommendation, - "Bars should not be rendered when there is no intent 'horsepower' specified.") + df = register_new_action() + df._repr_html_() + assert ( + "bars" not in df.recommendation, + "Bars should not be rendered when there is no intent 'horsepower' specified.", + ) + 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, - "Bars should be rendered when intent 'horsepower' is specified.") + df = register_new_action() + df.set_intent(["Acceleration", "Horsepower"]) + df._repr_html_() + assert len(df.recommendation["bars"]) > 0 + assert ( + "bars" in df.recommendation, + "Bars should be rendered when intent 'horsepower' is specified.", + ) + def test_no_validator(): - df = register_new_action(False) - df._repr_html_() - assert(len(df.recommendation["bars"]) > 0) - assert("bars" in df.recommendation) + df = register_new_action(False) + df._repr_html_() + assert len(df.recommendation["bars"]) > 0 + assert "bars" in df.recommendation + 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") + 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_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") + 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_remove_action(): - df = register_new_action() - df.set_intent(["Acceleration", "Horsepower"]) - df._repr_html_() - 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, - "Bars should not be rendered after it has been removed.") + df = register_new_action() + df.set_intent(["Acceleration", "Horsepower"]) + df._repr_html_() + 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, + "Bars should not be rendered after it has been removed.", + ) + 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") + 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_remove_default_actions(): - df = pd.read_csv("lux/data/car.csv") - df._repr_html_() + 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("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("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("Temporal") - df._repr_html_() - assert("Temporal" not in df.recommendation) + lux.remove_action("Correlation") + df._repr_html_() + assert "Correlation" not in df.recommendation - lux.remove_action("Correlation") - df._repr_html_() - assert("Correlation" not in df.recommendation) + assert ( + len(df.recommendation) == 0, + "Default actions should not be rendered after it has been removed.", + ) - assert(len(df.recommendation) == 0, - "Default actions should not be rendered after it has been removed.") + df = register_new_action() + df.set_intent(["Acceleration", "Horsepower"]) + df._repr_html_() + assert ( + "bars" in df.recommendation, + "Bars should be rendered after it has been registered with correct intent.", + ) + assert len(df.recommendation["bars"]) > 0 - df = register_new_action() - df.set_intent(["Acceleration", "Horsepower"]) - df._repr_html_() - assert("bars" in df.recommendation, - "Bars should be rendered after it has been registered with correct intent.") - assert(len(df.recommendation["bars"]) > 0) -# TODO: This test does not pass in pytest but is working in Jupyter notebook. +# TODO: This test does not pass in pytest but is working in Jupyter notebook. # def test_plot_setting(): # df = pd.read_csv("lux/data/car.csv") -# df["Year"] = pd.to_datetime(df["Year"], format='%Y') +# df["Year"] = pd.to_datetime(df["Year"], format='%Y') # def change_color_add_title(chart): # chart = chart.configure_mark(color="green") # change mark color to green # chart.title = "Custom Title" # add title to chart @@ -154,4 +192,4 @@ def test_remove_default_actions(): # vis_code = df.recommendation["Correlation"][0].to_Altair() # print (vis_code) -# assert 'chart = chart.configure_mark(color="green")' in vis_code, "Exported chart does not have additional plot style setting." \ No newline at end of file +# assert 'chart = chart.configure_mark(color="green")' in vis_code, "Exported chart does not have additional plot style setting." diff --git a/tests/test_dates.py b/tests/test_dates.py index 2ffef558..140a2450 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -1,5 +1,5 @@ # 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 @@ -19,78 +19,105 @@ from lux.utils import date_utils from lux.executor.PandasExecutor import PandasExecutor + def test_dateformatter(): - ldf = pd.read_csv("lux/data/car.csv") - ldf["Year"] = pd.to_datetime(ldf["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - timestamp = np.datetime64('2019-08-26') - ldf.maintain_metadata() - assert(date_utils.date_formatter(timestamp,ldf) == '2019') + ldf = pd.read_csv("lux/data/car.csv") + ldf["Year"] = pd.to_datetime( + ldf["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + timestamp = np.datetime64("2019-08-26") + ldf.maintain_metadata() + assert date_utils.date_formatter(timestamp, ldf) == "2019" - ldf["Year"][0] = np.datetime64('1970-03-01') # make month non unique + ldf["Year"][0] = np.datetime64("1970-03-01") # make month non unique - assert (date_utils.date_formatter(timestamp, ldf) == '2019-8') + assert date_utils.date_formatter(timestamp, ldf) == "2019-8" + ldf["Year"][0] = np.datetime64("1970-03-03") # make day non unique - ldf["Year"][0] = np.datetime64('1970-03-03') # make day non unique + assert date_utils.date_formatter(timestamp, ldf) == "2019-8-26" - assert (date_utils.date_formatter(timestamp, ldf) == '2019-8-26') def test_period_selection(): - ldf = pd.read_csv("lux/data/car.csv") - ldf["Year"] = pd.to_datetime(ldf["Year"], format='%Y') + ldf = pd.read_csv("lux/data/car.csv") + ldf["Year"] = pd.to_datetime(ldf["Year"], format="%Y") + + ldf["Year"] = pd.DatetimeIndex(ldf["Year"]).to_period(freq="A") - ldf["Year"] = pd.DatetimeIndex(ldf["Year"]).to_period(freq='A') + ldf.set_intent( + [ + lux.Clause(attribute=["Horsepower", "Weight", "Acceleration"]), + lux.Clause(attribute="Year"), + ] + ) - ldf.set_intent([lux.Clause(attribute = ["Horsepower", "Weight", "Acceleration"]), lux.Clause(attribute ="Year")]) + PandasExecutor.execute(ldf.current_vis, ldf) - PandasExecutor.execute(ldf.current_vis, ldf) + assert all( + [type(vlist.data) == lux.core.frame.LuxDataFrame for vlist in ldf.current_vis] + ) + assert all(ldf.current_vis[2].data.columns == ["Year", "Acceleration"]) - assert all([type(vlist.data) == lux.core.frame.LuxDataFrame for vlist in ldf.current_vis]) - assert all(ldf.current_vis[2].data.columns == ["Year", 'Acceleration']) def test_period_filter(): - ldf = pd.read_csv("lux/data/car.csv") - ldf["Year"] = pd.to_datetime(ldf["Year"], format='%Y') + ldf = pd.read_csv("lux/data/car.csv") + ldf["Year"] = pd.to_datetime(ldf["Year"], format="%Y") - ldf["Year"] = pd.DatetimeIndex(ldf["Year"]).to_period(freq='A') + ldf["Year"] = pd.DatetimeIndex(ldf["Year"]).to_period(freq="A") - ldf.set_intent([lux.Clause(attribute ="Acceleration"), lux.Clause(attribute ="Horsepower")]) + ldf.set_intent( + [lux.Clause(attribute="Acceleration"), lux.Clause(attribute="Horsepower")] + ) - PandasExecutor.execute(ldf.current_vis, ldf) - ldf._repr_html_() + PandasExecutor.execute(ldf.current_vis, ldf) + ldf._repr_html_() + + assert isinstance( + ldf.recommendation["Filter"][2]._inferred_intent[2].value, pd.Period + ) - assert isinstance(ldf.recommendation['Filter'][2]._inferred_intent[2].value, pd.Period) def test_period_to_altair(): - chart = None - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + chart = None + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + + df["Year"] = pd.DatetimeIndex(df["Year"]).to_period(freq="A") + + df.set_intent( + [lux.Clause(attribute="Acceleration"), lux.Clause(attribute="Horsepower")] + ) - df["Year"] = pd.DatetimeIndex(df["Year"]).to_period(freq='A') + PandasExecutor.execute(df.current_vis, df) + df._repr_html_() - df.set_intent([lux.Clause(attribute ="Acceleration"), lux.Clause(attribute ="Horsepower")]) + exported_code = df.recommendation["Filter"][2].to_Altair() - PandasExecutor.execute(df.current_vis, df) - df._repr_html_() + assert "Year = 1971" in exported_code - exported_code = df.recommendation['Filter'][2].to_Altair() - - assert 'Year = 1971' in exported_code def test_refresh_inplace(): - df = pd.DataFrame({'date': ['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01'], 'value': [10.5,15.2,20.3,25.2]}) - with pytest.warns(UserWarning,match="Lux detects that the attribute 'date' may be temporal."): - df._repr_html_() - assert df.data_type_lookup["date"]=="temporal" - - from lux.vis.Vis import Vis - vis = Vis(["date","value"],df) - - df['date'] = pd.to_datetime(df['date'],format="%Y-%m-%d") - df.maintain_metadata() - assert df.data_type['temporal'][0] == 'date' - - vis.refresh_source(df) - assert vis.mark == "line" - assert vis.get_attr_by_channel("x")[0].attribute == "date" - assert vis.get_attr_by_channel("y")[0].attribute == "value" \ No newline at end of file + df = pd.DataFrame( + { + "date": ["2020-01-01", "2020-02-01", "2020-03-01", "2020-04-01"], + "value": [10.5, 15.2, 20.3, 25.2], + } + ) + with pytest.warns( + UserWarning, match="Lux detects that the attribute 'date' may be temporal." + ): + df._repr_html_() + assert df.data_type_lookup["date"] == "temporal" + + from lux.vis.Vis import Vis + + vis = Vis(["date", "value"], df) + + df["date"] = pd.to_datetime(df["date"], format="%Y-%m-%d") + df.maintain_metadata() + assert df.data_type["temporal"][0] == "date" + + vis.refresh_source(df) + assert vis.mark == "line" + assert vis.get_attr_by_channel("x")[0].attribute == "date" + assert vis.get_attr_by_channel("y")[0].attribute == "value" diff --git a/tests/test_display.py b/tests/test_display.py index ef570d8d..54da6ca4 100644 --- a/tests/test_display.py +++ b/tests/test_display.py @@ -1,5 +1,5 @@ # 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 @@ -17,20 +17,25 @@ import pandas as pd from lux.vis.Vis import Vis from lux.vis.VisList import VisList + + def test_to_pandas(): df = pd.read_csv("lux/data/car.csv") df.to_pandas() + def test_display_LuxDataframe(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() - + + def test_display_Vis(): df = pd.read_csv("lux/data/car.csv") - vis = Vis(["Horsepower","Acceleration"],df) + vis = Vis(["Horsepower", "Acceleration"], df) vis._repr_html_() - + + def test_display_VisList(): df = pd.read_csv("lux/data/car.csv") - vislist = VisList(["?","Acceleration"],df) - vislist._repr_html_() \ No newline at end of file + vislist = VisList(["?", "Acceleration"], df) + vislist._repr_html_() diff --git a/tests/test_error_warning.py b/tests/test_error_warning.py index b745a751..e4e23c11 100644 --- a/tests/test_error_warning.py +++ b/tests/test_error_warning.py @@ -1,5 +1,5 @@ # 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 @@ -19,35 +19,41 @@ # Test suite for checking if the expected errors and warnings are showing up correctly def test_context_str_error(): df = pd.read_csv("lux/data/college.csv") - with pytest.raises(TypeError,match="Input intent must be a list"): + with pytest.raises(TypeError, match="Input intent must be a list"): df.set_intent("bad string input") + + def test_export_b4_widget_created(): df = pd.read_csv("lux/data/college.csv") - with pytest.warns(UserWarning,match="No widget attached to the dataframe"): + with pytest.warns(UserWarning, match="No widget attached to the dataframe"): df.exported + + def test_bad_filter(): df = pd.read_csv("lux/data/college.csv") - with pytest.warns(UserWarning,match="Lux can not operate on an empty dataframe"): - df[df["Region"]=="asdfgh"]._repr_html_() + with pytest.warns(UserWarning, match="Lux can not operate on an empty dataframe"): + df[df["Region"] == "asdfgh"]._repr_html_() + + # Test Properties with Private Variables Readable but not Writable def test_vis_private_properties(): from lux.vis.Vis import Vis + df = pd.read_csv("lux/data/car.csv") - vis = Vis(["Horsepower","Weight"],df) + vis = Vis(["Horsepower", "Weight"], df) vis._repr_html_() - assert isinstance(vis.data,lux.core.frame.LuxDataFrame) - with pytest.raises(AttributeError,match="can't set attribute"): + assert isinstance(vis.data, lux.core.frame.LuxDataFrame) + with pytest.raises(AttributeError, match="can't set attribute"): vis.data = "some val" - assert isinstance(vis.code,dict) - with pytest.raises(AttributeError,match="can't set attribute"): + assert isinstance(vis.code, dict) + with pytest.raises(AttributeError, match="can't set attribute"): vis.code = "some val" - - assert isinstance(vis.min_max,dict) - with pytest.raises(AttributeError,match="can't set attribute"): + + assert isinstance(vis.min_max, dict) + with pytest.raises(AttributeError, match="can't set attribute"): vis.min_max = "some val" - assert vis.mark =="scatter" - with pytest.raises(AttributeError,match="can't set attribute"): + assert vis.mark == "scatter" + with pytest.raises(AttributeError, match="can't set attribute"): vis.mark = "some val" - diff --git a/tests/test_executor.py b/tests/test_executor.py index f8ee2613..2dababb0 100644 --- a/tests/test_executor.py +++ b/tests/test_executor.py @@ -1,5 +1,5 @@ # 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 @@ -18,133 +18,203 @@ from lux.executor.PandasExecutor import PandasExecutor from lux.vis.Vis import Vis from lux.vis.VisList import VisList + + def test_lazy_execution(): df = pd.read_csv("lux/data/car.csv") - intent = [lux.Clause(attribute ="Horsepower", aggregation="mean"), lux.Clause(attribute ="Origin")] + intent = [ + lux.Clause(attribute="Horsepower", aggregation="mean"), + lux.Clause(attribute="Origin"), + ] vis = Vis(intent) # Check data field in vis is empty before calling executor assert vis.data is None PandasExecutor.execute([vis], df) assert type(vis.data) == lux.core.frame.LuxDataFrame - + + def test_selection(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - intent = [lux.Clause(attribute = ["Horsepower", "Weight", "Acceleration"]), lux.Clause(attribute ="Year")] - vislist = VisList(intent,df) + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + intent = [ + lux.Clause(attribute=["Horsepower", "Weight", "Acceleration"]), + lux.Clause(attribute="Year"), + ] + vislist = VisList(intent, df) assert all([type(vis.data) == lux.core.frame.LuxDataFrame for vis in vislist]) - assert all(vislist[2].data.columns == ["Year", 'Acceleration']) + assert all(vislist[2].data.columns == ["Year", "Acceleration"]) + def test_aggregation(): df = pd.read_csv("lux/data/car.csv") - intent = [lux.Clause(attribute ="Horsepower", aggregation="mean"), lux.Clause(attribute ="Origin")] - vis = Vis(intent,df) + intent = [ + lux.Clause(attribute="Horsepower", aggregation="mean"), + lux.Clause(attribute="Origin"), + ] + vis = Vis(intent, df) result_df = vis.data - assert int(result_df[result_df["Origin"]=="USA"]["Horsepower"])==119 + assert int(result_df[result_df["Origin"] == "USA"]["Horsepower"]) == 119 - intent = [lux.Clause(attribute ="Horsepower", aggregation="sum"), lux.Clause(attribute ="Origin")] - vis = Vis(intent,df) + intent = [ + lux.Clause(attribute="Horsepower", aggregation="sum"), + lux.Clause(attribute="Origin"), + ] + vis = Vis(intent, df) result_df = vis.data - assert int(result_df[result_df["Origin"]=="Japan"]["Horsepower"])==6307 + assert int(result_df[result_df["Origin"] == "Japan"]["Horsepower"]) == 6307 - intent = [lux.Clause(attribute ="Horsepower", aggregation="max"), lux.Clause(attribute ="Origin")] - vis = Vis(intent,df) + intent = [ + lux.Clause(attribute="Horsepower", aggregation="max"), + lux.Clause(attribute="Origin"), + ] + vis = Vis(intent, df) result_df = vis.data - assert int(result_df[result_df["Origin"]=="Europe"]["Horsepower"])==133 + assert int(result_df[result_df["Origin"] == "Europe"]["Horsepower"]) == 133 + def test_colored_bar_chart(): from lux.vis.Vis import Vis from lux.vis.Vis import Clause + df = pd.read_csv("lux/data/car.csv") - x_clause = Clause(attribute = "MilesPerGal", channel = "x") - y_clause = Clause(attribute = "Origin", channel = "y") - color_clause = Clause(attribute = 'Cylinders', channel = "color") + x_clause = Clause(attribute="MilesPerGal", channel="x") + y_clause = Clause(attribute="Origin", channel="y") + color_clause = Clause(attribute="Cylinders", channel="color") - new_vis = Vis([x_clause, y_clause, color_clause],df) - #make sure dimention of the data is correct - color_cardinality = len(df.unique_values['Cylinders']) - group_by_cardinality = len(df.unique_values['Origin']) - assert (len(new_vis.data.columns)==3) - assert(len(new_vis.data)==15 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values + new_vis = Vis([x_clause, y_clause, color_clause], df) + # make sure dimention of the data is correct + color_cardinality = len(df.unique_values["Cylinders"]) + group_by_cardinality = len(df.unique_values["Origin"]) + assert len(new_vis.data.columns) == 3 + assert ( + len(new_vis.data) + == 15 + > group_by_cardinality + < color_cardinality * group_by_cardinality + ) # Not color_cardinality*group_by_cardinality since some combinations have 0 values - def test_colored_line_chart(): from lux.vis.Vis import Vis from lux.vis.Vis import Clause + df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + + x_clause = Clause(attribute="Year", channel="x") + y_clause = Clause(attribute="MilesPerGal", channel="y") + color_clause = Clause(attribute="Cylinders", channel="color") - x_clause = Clause(attribute = "Year", channel = "x") - y_clause = Clause(attribute = "MilesPerGal", channel = "y") - color_clause = Clause(attribute = 'Cylinders', channel = "color") + new_vis = Vis([x_clause, y_clause, color_clause], df) - new_vis = Vis([x_clause, y_clause, color_clause],df) + # make sure dimention of the data is correct + color_cardinality = len(df.unique_values["Cylinders"]) + group_by_cardinality = len(df.unique_values["Year"]) + assert len(new_vis.data.columns) == 3 + assert ( + len(new_vis.data) + == 60 + > group_by_cardinality + < color_cardinality * group_by_cardinality + ) # Not color_cardinality*group_by_cardinality since some combinations have 0 values - #make sure dimention of the data is correct - color_cardinality = len(df.unique_values['Cylinders']) - group_by_cardinality = len(df.unique_values['Year']) - assert (len(new_vis.data.columns)==3) - assert(len(new_vis.data)==60 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values - def test_filter(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - intent = [lux.Clause(attribute ="Horsepower"), lux.Clause(attribute ="Year"), lux.Clause(attribute ="Origin", filter_op="=", value ="USA")] - vis = Vis(intent,df) + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + intent = [ + lux.Clause(attribute="Horsepower"), + lux.Clause(attribute="Year"), + lux.Clause(attribute="Origin", filter_op="=", value="USA"), + ] + vis = Vis(intent, df) vis._vis_data = df PandasExecutor.execute_filter(vis) - assert len(vis.data) == len(df[df["Origin"]=="USA"]) + assert len(vis.data) == len(df[df["Origin"] == "USA"]) + + def test_inequalityfilter(): df = pd.read_csv("lux/data/car.csv") - vis = Vis([lux.Clause(attribute ="Horsepower", filter_op=">", value=50), lux.Clause(attribute ="MilesPerGal")]) + vis = Vis( + [ + lux.Clause(attribute="Horsepower", filter_op=">", value=50), + lux.Clause(attribute="MilesPerGal"), + ] + ) vis._vis_data = df PandasExecutor.execute_filter(vis) assert len(df) > len(vis.data) - assert len(vis.data) == 386 - - intent = [lux.Clause(attribute ="Horsepower", filter_op="<=", value=100), lux.Clause(attribute ="MilesPerGal")] - vis = Vis(intent,df) + assert len(vis.data) == 386 + + intent = [ + lux.Clause(attribute="Horsepower", filter_op="<=", value=100), + lux.Clause(attribute="MilesPerGal"), + ] + vis = Vis(intent, df) vis._vis_data = df PandasExecutor.execute_filter(vis) - assert len(vis.data) == len(df[df["Horsepower"]<=100]) == 242 + assert len(vis.data) == len(df[df["Horsepower"] <= 100]) == 242 # Test end-to-end PandasExecutor.execute([vis], df) - Nbins =list(filter(lambda x: x.bin_size!=0, vis._inferred_intent))[0].bin_size + Nbins = list(filter(lambda x: x.bin_size != 0, vis._inferred_intent))[0].bin_size assert len(vis.data) == Nbins - + + def test_binning(): df = pd.read_csv("lux/data/car.csv") - vis = Vis([lux.Clause(attribute ="Horsepower")],df) - nbins =list(filter(lambda x: x.bin_size!=0, vis._inferred_intent))[0].bin_size + vis = Vis([lux.Clause(attribute="Horsepower")], df) + nbins = list(filter(lambda x: x.bin_size != 0, vis._inferred_intent))[0].bin_size assert len(vis.data) == nbins + def test_record(): df = pd.read_csv("lux/data/car.csv") - vis = Vis([lux.Clause(attribute ="Cylinders")],df) + vis = Vis([lux.Clause(attribute="Cylinders")], df) assert len(vis.data) == len(df["Cylinders"].unique()) - + + def test_filter_aggregation_fillzero_aligned(): df = pd.read_csv("lux/data/car.csv") - intent = [lux.Clause(attribute="Cylinders"), lux.Clause(attribute="MilesPerGal"), lux.Clause("Origin=Japan")] - vis = Vis(intent,df) + intent = [ + lux.Clause(attribute="Cylinders"), + lux.Clause(attribute="MilesPerGal"), + lux.Clause("Origin=Japan"), + ] + vis = Vis(intent, df) result = vis.data - externalValidation = df[df["Origin"]=="Japan"].groupby("Cylinders").mean()["MilesPerGal"] - assert result[result["Cylinders"]==5]["MilesPerGal"].values[0]==0 - assert result[result["Cylinders"]==8]["MilesPerGal"].values[0]==0 - assert result[result["Cylinders"]==3]["MilesPerGal"].values[0]==externalValidation[3] - assert result[result["Cylinders"]==4]["MilesPerGal"].values[0]==externalValidation[4] - assert result[result["Cylinders"]==6]["MilesPerGal"].values[0]==externalValidation[6] + externalValidation = ( + df[df["Origin"] == "Japan"].groupby("Cylinders").mean()["MilesPerGal"] + ) + assert result[result["Cylinders"] == 5]["MilesPerGal"].values[0] == 0 + assert result[result["Cylinders"] == 8]["MilesPerGal"].values[0] == 0 + assert ( + result[result["Cylinders"] == 3]["MilesPerGal"].values[0] + == externalValidation[3] + ) + assert ( + result[result["Cylinders"] == 4]["MilesPerGal"].values[0] + == externalValidation[4] + ) + assert ( + result[result["Cylinders"] == 6]["MilesPerGal"].values[0] + == externalValidation[6] + ) + def test_exclude_attribute(): df = pd.read_csv("lux/data/car.csv") intent = [lux.Clause("?", exclude=["Name", "Year"]), lux.Clause("Horsepower")] - vislist = VisList(intent,df) + vislist = VisList(intent, df) for vis in vislist: - assert (vis.get_attr_by_channel("x")[0].attribute != "Year") - assert (vis.get_attr_by_channel("x")[0].attribute != "Name") - assert (vis.get_attr_by_channel("y")[0].attribute != "Year") - assert (vis.get_attr_by_channel("y")[0].attribute != "Year") + assert vis.get_attr_by_channel("x")[0].attribute != "Year" + assert vis.get_attr_by_channel("x")[0].attribute != "Name" + assert vis.get_attr_by_channel("y")[0].attribute != "Year" + assert vis.get_attr_by_channel("y")[0].attribute != "Year" diff --git a/tests/test_interestingness.py b/tests/test_interestingness.py index 7943e95f..7fa7fcd0 100644 --- a/tests/test_interestingness.py +++ b/tests/test_interestingness.py @@ -1,5 +1,5 @@ # 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 @@ -21,143 +21,187 @@ # The following test cases are labelled for vis with def test_interestingness_1_0_0(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - - df.set_intent([lux.Clause(attribute = "Origin")]) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + + df.set_intent([lux.Clause(attribute="Origin")]) df._repr_html_() - #check that top recommended enhance graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended enhance graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Enhance"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Enhance'])): - vis = df.recommendation['Enhance'][f] - if vis.get_attr_by_channel("x")[0].attribute == 'Displacement': + for f in range(0, len(df.recommendation["Enhance"])): + vis = df.recommendation["Enhance"][f] + if vis.get_attr_by_channel("x")[0].attribute == "Displacement": rank1 = f - if vis.get_attr_by_channel("x")[0].attribute == 'Weight': + if vis.get_attr_by_channel("x")[0].attribute == "Weight": rank2 = f - if vis.get_attr_by_channel("x")[0].attribute == 'Acceleration': + if vis.get_attr_by_channel("x")[0].attribute == "Acceleration": rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended filter graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended filter graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Filter"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Filter'])): - vis = df.recommendation['Filter'][f] - if len(vis.get_attr_by_attr_name("Cylinders"))>0: + for f in range(0, len(df.recommendation["Filter"])): + vis = df.recommendation["Filter"][f] + if len(vis.get_attr_by_attr_name("Cylinders")) > 0: if int(vis._inferred_intent[2].value) == 8: rank1 = f if int(vis._inferred_intent[2].value) == 6: rank2 = f - if '1972' in str(df.recommendation['Filter'][f]._inferred_intent[2].value): + if "1972" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 + + def test_interestingness_1_0_1(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Origin", filter_op="=",value="USA"),lux.Clause(attribute = "Cylinders")]) + df.set_intent( + [ + lux.Clause(attribute="Origin", filter_op="=", value="USA"), + lux.Clause(attribute="Cylinders"), + ] + ) df._repr_html_() assert df.current_vis[0].score == 0 + def test_interestingness_0_1_0(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower")]) + df.set_intent([lux.Clause(attribute="Horsepower")]) df._repr_html_() - #check that top recommended enhance graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended enhance graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Enhance"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Enhance'])): - if df.recommendation['Enhance'][f].mark == 'scatter' and df.recommendation['Enhance'][f]._inferred_intent[1].attribute == 'Weight': + for f in range(0, len(df.recommendation["Enhance"])): + if ( + df.recommendation["Enhance"][f].mark == "scatter" + and df.recommendation["Enhance"][f]._inferred_intent[1].attribute + == "Weight" + ): rank1 = f - if df.recommendation['Enhance'][f].mark == 'scatter' and df.recommendation['Enhance'][f]._inferred_intent[1].attribute == 'Acceleration': + if ( + df.recommendation["Enhance"][f].mark == "scatter" + and df.recommendation["Enhance"][f]._inferred_intent[1].attribute + == "Acceleration" + ): rank2 = f - if df.recommendation['Enhance'][f].mark == 'line' and df.recommendation['Enhance'][f]._inferred_intent[0].attribute == 'Year': + if ( + df.recommendation["Enhance"][f].mark == "line" + and df.recommendation["Enhance"][f]._inferred_intent[0].attribute == "Year" + ): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended filter graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended filter graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Filter"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Filter'])): - if df.recommendation['Filter'][f]._inferred_intent[2].value == 4: + for f in range(0, len(df.recommendation["Filter"])): + if df.recommendation["Filter"][f]._inferred_intent[2].value == 4: rank1 = f - if str(df.recommendation['Filter'][f]._inferred_intent[2].value) == "Europe": + if str(df.recommendation["Filter"][f]._inferred_intent[2].value) == "Europe": rank2 = f - if '1971' in str(df.recommendation['Filter'][f]._inferred_intent[2].value): + if "1971" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 def test_interestingness_0_1_1(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - - df.set_intent([lux.Clause(attribute = "Origin", filter_op="=",value="?"),lux.Clause(attribute = "MilesPerGal")]) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + + df.set_intent( + [ + lux.Clause(attribute="Origin", filter_op="=", value="?"), + lux.Clause(attribute="MilesPerGal"), + ] + ) df._repr_html_() - assert interestingness(df.recommendation['Current Vis'][0],df) != None - assert str(df.recommendation['Current Vis'][0]._inferred_intent[2].value) == 'USA' + assert interestingness(df.recommendation["Current Vis"][0], df) != None + assert str(df.recommendation["Current Vis"][0]._inferred_intent[2].value) == "USA" + def test_interestingness_1_1_0(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower"),lux.Clause(attribute = "Year")]) + df.set_intent([lux.Clause(attribute="Horsepower"), lux.Clause(attribute="Year")]) df._repr_html_() - #check that top recommended Enhance graph score is not none (all graphs here have same score) - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended Enhance graph score is not none (all graphs here have same score) + assert interestingness(df.recommendation["Enhance"][0], df) != None - #check that top recommended filter graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended filter graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Filter"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Filter'])): - vis = df.recommendation['Filter'][f] - if len(vis.get_attr_by_attr_name("Cylinders"))>0: + for f in range(0, len(df.recommendation["Filter"])): + vis = df.recommendation["Filter"][f] + if len(vis.get_attr_by_attr_name("Cylinders")) > 0: if int(vis._inferred_intent[2].value) == 6: rank1 = f if int(vis._inferred_intent[2].value) == 5: rank3 = f - if len(vis.get_attr_by_attr_name("Origin"))>0: + if len(vis.get_attr_by_attr_name("Origin")) > 0: if str(vis._inferred_intent[2].value) == "Europe": rank2 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended generalize graph score is not none - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended generalize graph score is not none + assert interestingness(df.recommendation["Filter"][0], df) != None + def test_interestingness_1_1_1(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower"), lux.Clause(attribute = "Origin", filter_op="=",value = "USA", bin_size=20)]) + df.set_intent( + [ + lux.Clause(attribute="Horsepower"), + lux.Clause(attribute="Origin", filter_op="=", value="USA", bin_size=20), + ] + ) df._repr_html_() - #check that top recommended Enhance graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended Enhance graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Enhance"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Enhance'])): - if str(df.recommendation['Enhance'][f]._inferred_intent[2].value) == "USA" and str(df.recommendation['Enhance'][f]._inferred_intent[1].attribute) == 'Cylinders': + for f in range(0, len(df.recommendation["Enhance"])): + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].value) == "USA" + and str(df.recommendation["Enhance"][f]._inferred_intent[1].attribute) + == "Cylinders" + ): rank1 = f - if str(df.recommendation['Enhance'][f]._inferred_intent[2].value) == "USA" and str(df.recommendation['Enhance'][f]._inferred_intent[1].attribute) == 'Weight': + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].value) == "USA" + and str(df.recommendation["Enhance"][f]._inferred_intent[1].attribute) + == "Weight" + ): rank2 = f - if str(df.recommendation['Enhance'][f]._inferred_intent[2].value) == "USA" and str(df.recommendation['Enhance'][f]._inferred_intent[1].attribute) == 'Horsepower': + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].value) == "USA" + and str(df.recommendation["Enhance"][f]._inferred_intent[1].attribute) + == "Horsepower" + ): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check for top recommended Filter graph score is not none - assert interestingness(df.recommendation['Filter'][0],df) != None + # check for top recommended Filter graph score is not none + assert interestingness(df.recommendation["Filter"][0], df) != None + def test_interestingness_1_2_0(): from lux.vis.Vis import Vis @@ -165,59 +209,79 @@ def test_interestingness_1_2_0(): from lux.interestingness.interestingness import interestingness df = pd.read_csv("lux/data/car.csv") - y_clause = Clause(attribute = "Name", channel = "y") - color_clause = Clause(attribute = 'Cylinders', channel = "color") + y_clause = Clause(attribute="Name", channel="y") + color_clause = Clause(attribute="Cylinders", channel="color") new_vis = Vis([y_clause, color_clause]) new_vis.refresh_source(df) new_vis - #assert(len(new_vis.data)==color_cardinality*group_by_cardinality) + # assert(len(new_vis.data)==color_cardinality*group_by_cardinality) + + assert interestingness(new_vis, df) < 0.01 - assert(interestingness(new_vis, df)<0.01) def test_interestingness_0_2_0(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower"),lux.Clause(attribute = "Acceleration")]) + df.set_intent( + [lux.Clause(attribute="Horsepower"), lux.Clause(attribute="Acceleration")] + ) df._repr_html_() - #check that top recommended enhance graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended enhance graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Enhance"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Enhance'])): - if str(df.recommendation['Enhance'][f]._inferred_intent[2].attribute) == "Origin" and str(df.recommendation['Enhance'][f].mark) == 'scatter': + for f in range(0, len(df.recommendation["Enhance"])): + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].attribute) + == "Origin" + and str(df.recommendation["Enhance"][f].mark) == "scatter" + ): rank1 = f - if str(df.recommendation['Enhance'][f]._inferred_intent[2].attribute) == "Displacement" and str(df.recommendation['Enhance'][f].mark) == 'scatter': + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].attribute) + == "Displacement" + and str(df.recommendation["Enhance"][f].mark) == "scatter" + ): rank2 = f - if str(df.recommendation['Enhance'][f]._inferred_intent[2].attribute) == "Year" and str(df.recommendation['Enhance'][f].mark) == 'scatter': + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].attribute) == "Year" + and str(df.recommendation["Enhance"][f].mark) == "scatter" + ): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended filter graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended filter graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Filter"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Filter'])): - if '1973' in str(df.recommendation['Filter'][f]._inferred_intent[2].value): + for f in range(0, len(df.recommendation["Filter"])): + if "1973" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank1 = f - if '1976' in str(df.recommendation['Filter'][f]._inferred_intent[2].value): + if "1976" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank2 = f - if str(df.recommendation['Filter'][f]._inferred_intent[2].value) == "Europe": + if str(df.recommendation["Filter"][f]._inferred_intent[2].value) == "Europe": rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended Generalize graph score is not none - assert interestingness(df.recommendation['Generalize'][0],df) != None + # check that top recommended Generalize graph score is not none + assert interestingness(df.recommendation["Generalize"][0], df) != None def test_interestingness_0_2_1(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower"),lux.Clause(attribute = "MilesPerGal"),lux.Clause(attribute = "Acceleration", filter_op=">",value = 10)]) + df.set_intent( + [ + lux.Clause(attribute="Horsepower"), + lux.Clause(attribute="MilesPerGal"), + lux.Clause(attribute="Acceleration", filter_op=">", value=10), + ] + ) df._repr_html_() - #check that top recommended Generalize graph score is not none - assert interestingness(df.recommendation['Generalize'][0],df) != None \ No newline at end of file + # check that top recommended Generalize graph score is not none + assert interestingness(df.recommendation["Generalize"][0], df) != None diff --git a/tests/test_maintainence.py b/tests/test_maintainence.py index 797d3462..20ee227e 100644 --- a/tests/test_maintainence.py +++ b/tests/test_maintainence.py @@ -1,5 +1,5 @@ # 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 @@ -18,58 +18,71 @@ import pandas as pd from lux.vis.Vis import Vis + def test_metadata_subsequent_display(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() - assert df._metadata_fresh==True, "Failed to maintain metadata after display df" + assert df._metadata_fresh == True, "Failed to maintain metadata after display df" df._repr_html_() - assert df._metadata_fresh==True, "Failed to maintain metadata after display df" + assert df._metadata_fresh == True, "Failed to maintain metadata after display df" + def test_metadata_subsequent_vis(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() - assert df._metadata_fresh==True, "Failed to maintain metadata after display df" - vis = Vis(["Acceleration","Horsepower"],df) - assert df._metadata_fresh==True, "Failed to maintain metadata after display df" + assert df._metadata_fresh == True, "Failed to maintain metadata after display df" + vis = Vis(["Acceleration", "Horsepower"], df) + assert df._metadata_fresh == True, "Failed to maintain metadata after display df" + def test_metadata_inplace_operation(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() - assert df._metadata_fresh==True, "Failed to maintain metadata after display df" + assert df._metadata_fresh == True, "Failed to maintain metadata after display df" df.dropna(inplace=True) - assert df._metadata_fresh==False, "Failed to expire metadata after in-place Pandas operation" + assert ( + df._metadata_fresh == False + ), "Failed to expire metadata after in-place Pandas operation" + def test_metadata_new_df_operation(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() - assert df._metadata_fresh==True, "Failed to maintain metadata after display df" - df[["MilesPerGal","Acceleration"]] - assert df._metadata_fresh==True, "Failed to maintain metadata after display df" - df2 = df[["MilesPerGal","Acceleration"]] - assert not hasattr(df2,"_metadata_fresh") + assert df._metadata_fresh == True, "Failed to maintain metadata after display df" + df[["MilesPerGal", "Acceleration"]] + assert df._metadata_fresh == True, "Failed to maintain metadata after display df" + df2 = df[["MilesPerGal", "Acceleration"]] + assert not hasattr(df2, "_metadata_fresh") + def test_metadata_column_group_reset_df(): df = pd.read_csv("lux/data/car.csv") - assert not hasattr(df,"_metadata_fresh") - df['Year'] = pd.to_datetime(df['Year'], format='%Y') - assert hasattr(df,"_metadata_fresh") + assert not hasattr(df, "_metadata_fresh") + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + assert hasattr(df, "_metadata_fresh") result = df.groupby("Cylinders").mean() - assert not hasattr(result,"_metadata_fresh") - result._repr_html_() # Note that this should trigger two compute metadata (one for df, and one for an intermediate df.reset_index used to feed inside created Vis) - assert result._metadata_fresh==True, "Failed to maintain metadata after display df" + assert not hasattr(result, "_metadata_fresh") + result._repr_html_() # Note that this should trigger two compute metadata (one for df, and one for an intermediate df.reset_index used to feed inside created Vis) + assert ( + result._metadata_fresh == True + ), "Failed to maintain metadata after display df" colgroup_recs = result.recommendation["Column Groups"] - assert len(colgroup_recs) == 5 - for rec in colgroup_recs: assert rec.mark=="bar", "Column Group not displaying bar charts" - + assert len(colgroup_recs) == 5 + for rec in colgroup_recs: + assert rec.mark == "bar", "Column Group not displaying bar charts" + + def test_recs_inplace_operation(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() - assert df._recs_fresh==True, "Failed to maintain recommendation after display df" - assert len(df.recommendation["Occurrence"])==3 - df.drop(columns=["Name"],inplace=True) - assert 'Name' not in df.columns, "Failed to perform `drop` operation in-place" - assert df._recs_fresh==False, "Failed to maintain recommendation after in-place Pandas operation" + assert df._recs_fresh == True, "Failed to maintain recommendation after display df" + assert len(df.recommendation["Occurrence"]) == 3 + df.drop(columns=["Name"], inplace=True) + assert "Name" not in df.columns, "Failed to perform `drop` operation in-place" + assert ( + df._recs_fresh == False + ), "Failed to maintain recommendation after in-place Pandas operation" df._repr_html_() - assert len(df.recommendation["Occurrence"])==2 - assert df._recs_fresh==True, "Failed to maintain recommendation after display df" \ No newline at end of file + assert len(df.recommendation["Occurrence"]) == 2 + assert df._recs_fresh == True, "Failed to maintain recommendation after display df" diff --git a/tests/test_nan.py b/tests/test_nan.py index c92a9097..df2b8e9f 100644 --- a/tests/test_nan.py +++ b/tests/test_nan.py @@ -1,5 +1,5 @@ # 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 @@ -18,10 +18,12 @@ import numpy as np from lux.vis.Vis import Vis + + def test_nan_column(): - df = pd.read_csv("lux/data/college.csv") - df["Geography"] = np.nan - df._repr_html_() - for visList in df.recommendation.keys(): - for vis in df.recommendation[visList]: - assert vis.get_attr_by_attr_name("Geography")==[] \ No newline at end of file + df = pd.read_csv("lux/data/college.csv") + df["Geography"] = np.nan + df._repr_html_() + for visList in df.recommendation.keys(): + for vis in df.recommendation[visList]: + assert vis.get_attr_by_attr_name("Geography") == [] diff --git a/tests/test_pandas.py b/tests/test_pandas.py index 114206e5..60f6a91c 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -1,5 +1,5 @@ # 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 @@ -27,13 +27,20 @@ # assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." # assert series.name == "Weight", "Pandas Series original `name` property not retained." + def test_head_tail(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() - assert df._message.to_html()=="" + assert df._message.to_html() == "" df.head()._repr_html_() - assert "Lux is visualizing the previous version of the dataframe before you applied head."in df._message.to_html() + assert ( + "Lux is visualizing the previous version of the dataframe before you applied head." + in df._message.to_html() + ) df._repr_html_() - assert df._message.to_html()=="" + assert df._message.to_html() == "" df.tail()._repr_html_() - assert "Lux is visualizing the previous version of the dataframe before you applied tail." in df._message.to_html() \ No newline at end of file + assert ( + "Lux is visualizing the previous version of the dataframe before you applied tail." + in df._message.to_html() + ) diff --git a/tests/test_pandas_coverage.py b/tests/test_pandas_coverage.py index c4d47616..d88badf5 100644 --- a/tests/test_pandas_coverage.py +++ b/tests/test_pandas_coverage.py @@ -1,5 +1,5 @@ # 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 @@ -20,24 +20,29 @@ # DataFrame Tests # ################### + def test_deepcopy(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - df._repr_html_(); + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + df._repr_html_() saved_df = df.copy(deep=True) - saved_df._repr_html_(); + saved_df._repr_html_() check_metadata_equal(df, saved_df) + def test_rename_inplace(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - df._repr_html_(); + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + df._repr_html_() new_df = df.copy(deep=True) - df.rename(columns={"Name": "Car Name"}, inplace = True) - df._repr_html_(); - new_df._repr_html_(); - new_df, df = df, new_df # new_df is the old dataframe (df) with the new column name changed inplace - + df.rename(columns={"Name": "Car Name"}, inplace=True) + df._repr_html_() + new_df._repr_html_() + new_df, df = ( + df, + new_df, + ) # new_df is the old dataframe (df) with the new column name changed inplace + assert df.data_type_lookup != new_df.data_type_lookup assert df.data_type_lookup["Name"] == new_df.data_type_lookup["Car Name"] @@ -60,12 +65,14 @@ def test_rename_inplace(): assert list(df.cardinality.values()) == list(new_df.cardinality.values()) assert df._min_max == new_df._min_max assert df.pre_aggregated == new_df.pre_aggregated + + def test_rename(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - df._repr_html_(); - new_df = df.rename(columns={"Name": "Car Name"}, inplace = False) - new_df._repr_html_(); + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + df._repr_html_() + new_df = df.rename(columns={"Name": "Car Name"}, inplace=False) + new_df._repr_html_() assert df.data_type_lookup != new_df.data_type_lookup assert df.data_type_lookup["Name"] == new_df.data_type_lookup["Car Name"] @@ -88,52 +95,84 @@ def test_rename(): assert list(df.cardinality.values()) == list(new_df.cardinality.values()) assert df._min_max == new_df._min_max assert df.pre_aggregated == new_df.pre_aggregated + + def test_rename3(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - df.columns = ["col1", "col2", "col3", "col4","col5", "col6", "col7", "col8", "col9"] + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + df.columns = [ + "col1", + "col2", + "col3", + "col4", + "col5", + "col6", + "col7", + "col8", + "col9", + ] df._repr_html_() - assert list(df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(df.cardinality) == 9 assert "col2" in list(df.cardinality.keys()) + def test_concat(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - new_df = pd.concat([df.loc[:, "Name":"Cylinders"], df.loc[:, "Year":"Origin"]], axis = "columns") + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + new_df = pd.concat( + [df.loc[:, "Name":"Cylinders"], df.loc[:, "Year":"Origin"]], axis="columns" + ) new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Distribution', 'Occurrence', 'Temporal'] + assert list(new_df.recommendation.keys()) == [ + "Distribution", + "Occurrence", + "Temporal", + ] assert len(new_df.cardinality) == 5 + def test_groupby_agg(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") new_df = df.groupby("Year").agg(sum) new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Column Groups'] + assert list(new_df.recommendation.keys()) == ["Column Groups"] assert len(new_df.cardinality) == 7 + def test_qcut(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - df["Weight"] = pd.qcut(df["Weight"], q = 3) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + df["Weight"] = pd.qcut(df["Weight"], q=3) df._repr_html_() + def test_cut(): df = pd.read_csv("lux/data/car.csv") - df["Weight"] = pd.cut(df["Weight"], bins = [0, 2500, 7500, 10000], labels = ["small", "medium", "large"]) + df["Weight"] = pd.cut( + df["Weight"], bins=[0, 2500, 7500, 10000], labels=["small", "medium", "large"] + ) df._repr_html_() + + def test_groupby_agg_very_small(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") new_df = df.groupby("Origin").agg(sum).reset_index() new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Column Groups'] + assert list(new_df.recommendation.keys()) == ["Column Groups"] assert len(new_df.cardinality) == 7 + # def test_groupby_multi_index(): # url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' # df = pd.read_csv(url) @@ -143,151 +182,244 @@ def test_groupby_agg_very_small(): # assert list(new_df.recommendation.keys() ) == ['Column Groups'] # TODO # assert len(new_df.cardinality) == 7 # TODO + def test_query(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") new_df = df.query("Weight > 3000") new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(new_df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(new_df.cardinality) == 9 + def test_pop(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") df.pop("Weight") df._repr_html_() - assert list(df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(df.cardinality) == 8 + def test_transform(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - new_df = df.iloc[:,1:].groupby("Origin").transform(sum) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + new_df = df.iloc[:, 1:].groupby("Origin").transform(sum) new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Occurrence'] + assert list(new_df.recommendation.keys()) == ["Correlation", "Occurrence"] assert len(new_df.cardinality) == 6 + def test_get_group(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") gbobj = df.groupby("Origin") new_df = gbobj.get_group("Japan") new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(new_df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(new_df.cardinality) == 9 + def test_applymap(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - mapping = {"USA": 0, "Europe": 1, "Japan":2} + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + mapping = {"USA": 0, "Europe": 1, "Japan": 2} df["Origin"] = df[["Origin"]].applymap(mapping.get) df._repr_html_() - assert list(df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(df.cardinality) == 9 + def test_strcat(): - df = pd.read_csv('https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true') - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - df["combined"] = df["Origin"].str.cat(df["Brand"], sep = ", ") + df = pd.read_csv( + "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" + ) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + df["combined"] = df["Origin"].str.cat(df["Brand"], sep=", ") df._repr_html_() - assert list(df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(df.cardinality) == 11 + def test_named_agg(): - df = pd.read_csv('https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true') - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - new_df = df.groupby("Brand").agg(avg_weight = ("Weight", "mean"), max_weight = ("Weight", "max"), mean_displacement = ("Displacement", "mean")) + df = pd.read_csv( + "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" + ) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + new_df = df.groupby("Brand").agg( + avg_weight=("Weight", "mean"), + max_weight=("Weight", "max"), + mean_displacement=("Displacement", "mean"), + ) new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Column Groups'] + assert list(new_df.recommendation.keys()) == ["Column Groups"] assert len(new_df.cardinality) == 4 + def test_change_dtype(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - df["Cylinders"] = pd.Series(df["Cylinders"], dtype = "Int64") + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + df["Cylinders"] = pd.Series(df["Cylinders"], dtype="Int64") df._repr_html_() - assert list(df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(df.data_type_lookup) == 9 + def test_get_dummies(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") new_df = pd.get_dummies(df) new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(new_df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(new_df.data_type_lookup) == 310 + def test_drop(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - new_df = df.drop([0, 1, 2], axis = "rows") - new_df2 = new_df.drop(["Name", "MilesPerGal", "Cylinders"], axis = "columns") + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + new_df = df.drop([0, 1, 2], axis="rows") + new_df2 = new_df.drop(["Name", "MilesPerGal", "Cylinders"], axis="columns") new_df2._repr_html_() - assert list(new_df2.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(new_df2.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(new_df2.cardinality) == 6 + def test_merge(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - new_df = df.drop([0, 1, 2], axis = "rows") - new_df2 = pd.merge(df, new_df, how = "left", indicator = True) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + new_df = df.drop([0, 1, 2], axis="rows") + new_df2 = pd.merge(df, new_df, how="left", indicator=True) new_df2._repr_html_() - assert list(new_df2.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] # TODO once bug is fixed + assert list(new_df2.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] # TODO once bug is fixed assert len(new_df2.cardinality) == 10 + def test_prefix(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") new_df = df.add_prefix("1_") new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(new_df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(new_df.cardinality) == 9 assert new_df.cardinality["1_Name"] == 300 + def test_loc(): - df = pd.read_csv('https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true') - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - new_df = df.loc[:,"Displacement":"Origin"] + df = pd.read_csv( + "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" + ) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + new_df = df.loc[:, "Displacement":"Origin"] new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(new_df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(new_df.cardinality) == 6 - new_df = df.loc[0:10,"Displacement":"Origin"] + new_df = df.loc[0:10, "Displacement":"Origin"] new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution'] + assert list(new_df.recommendation.keys()) == ["Correlation", "Distribution"] assert len(new_df.cardinality) == 6 - new_df = df.loc[0:10,"Displacement":"Horsepower"] + new_df = df.loc[0:10, "Displacement":"Horsepower"] new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution'] + assert list(new_df.recommendation.keys()) == ["Correlation", "Distribution"] assert len(new_df.cardinality) == 2 import numpy as np - inter_df = df.groupby("Brand")[["Acceleration", "Weight", "Horsepower"]].agg(np.mean) + + inter_df = df.groupby("Brand")[["Acceleration", "Weight", "Horsepower"]].agg( + np.mean + ) new_df = inter_df.loc["chevrolet":"fiat", "Acceleration":"Weight"] new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Column Groups'] + assert list(new_df.recommendation.keys()) == ["Column Groups"] assert len(new_df.cardinality) == 3 + def test_iloc(): - df = pd.read_csv('https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true') - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - new_df = df.iloc[:,3:9] + df = pd.read_csv( + "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" + ) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + new_df = df.iloc[:, 3:9] new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution', 'Occurrence', 'Temporal'] + assert list(new_df.recommendation.keys()) == [ + "Correlation", + "Distribution", + "Occurrence", + "Temporal", + ] assert len(new_df.cardinality) == 6 - new_df = df.iloc[0:11,3:9] + new_df = df.iloc[0:11, 3:9] new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution'] + assert list(new_df.recommendation.keys()) == ["Correlation", "Distribution"] assert len(new_df.cardinality) == 6 - new_df = df.iloc[0:11,3:5] + new_df = df.iloc[0:11, 3:5] new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Correlation', 'Distribution'] + assert list(new_df.recommendation.keys()) == ["Correlation", "Distribution"] assert len(new_df.cardinality) == 2 import numpy as np - inter_df = df.groupby("Brand")[["Acceleration", "Weight", "Horsepower"]].agg(np.mean) + + inter_df = df.groupby("Brand")[["Acceleration", "Weight", "Horsepower"]].agg( + np.mean + ) new_df = inter_df.iloc[5:10, 0:2] new_df._repr_html_() - assert list(new_df.recommendation.keys() ) == ['Column Groups'] + assert list(new_df.recommendation.keys()) == ["Column Groups"] assert len(new_df.cardinality) == 3 + def check_metadata_equal(df1, df2): # Checks to make sure metadata for df1 and df2 are equal. for attr in df1._metadata: @@ -335,6 +467,7 @@ def compare_clauses(clause1, clause2): assert clause1.sort == clause2.sort assert clause1.exclude == clause2.exclude + def compare_vis(vis1, vis2): assert len(vis1._intent) == len(vis2._intent) for j in range(len(vis1._intent)): @@ -342,7 +475,7 @@ def compare_vis(vis1, vis2): assert len(vis1._inferred_intent) == len(vis2._inferred_intent) for j in range(len(vis1._inferred_intent)): compare_clauses(vis1._inferred_intent[j], vis2._inferred_intent[j]) - assert vis1._source == vis2._source + assert vis1._source == vis2._source assert vis1._code == vis2._code assert vis1._mark == vis2._mark assert vis1._min_max == vis2._min_max @@ -350,41 +483,116 @@ def compare_vis(vis1, vis2): assert vis1.title == vis2.title assert vis1.score == vis2.score + ################ # Series Tests # ################ + def test_df_to_series(): # Ensure metadata is kept when going from df to series df = pd.read_csv("lux/data/car.csv") - df._repr_html_() # compute metadata + df._repr_html_() # compute metadata assert df.cardinality is not None series = df["Weight"] - assert isinstance(series,lux.core.series.LuxSeries), "Derived series is type LuxSeries." + assert isinstance( + series, lux.core.series.LuxSeries + ), "Derived series is type LuxSeries." df["Weight"]._metadata - assert df["Weight"]._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'], "Metadata is lost when going from Dataframe to Series." - assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." - assert series.name == "Weight", "Pandas Series original `name` property not retained." + assert df["Weight"]._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", + ], "Metadata is lost when going from Dataframe to Series." + assert ( + df.cardinality is not None + ), "Metadata is lost when going from Dataframe to Series." + assert ( + series.name == "Weight" + ), "Pandas Series original `name` property not retained." + def test_value_counts(): df = pd.read_csv("lux/data/car.csv") - df._repr_html_() # compute metadata + df._repr_html_() # compute metadata assert df.cardinality is not None series = df["Weight"] series.value_counts() - assert isinstance(series,lux.core.series.LuxSeries), "Derived series is type LuxSeries." - assert df["Weight"]._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'], "Metadata is lost when going from Dataframe to Series." - assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." - assert series.name == "Weight", "Pandas Series original `name` property not retained." + assert isinstance( + series, lux.core.series.LuxSeries + ), "Derived series is type LuxSeries." + assert df["Weight"]._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", + ], "Metadata is lost when going from Dataframe to Series." + assert ( + df.cardinality is not None + ), "Metadata is lost when going from Dataframe to Series." + assert ( + series.name == "Weight" + ), "Pandas Series original `name` property not retained." + def test_str_replace(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' + url = "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" df = pd.read_csv(url) - df._repr_html_() # compute metadata + df._repr_html_() # compute metadata assert df.cardinality is not None series = df["Brand"].str.replace("chevrolet", "chevy") - assert isinstance(series,lux.core.series.LuxSeries), "Derived series is type LuxSeries." - assert df["Brand"]._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'], "Metadata is lost when going from Dataframe to Series." - assert df.cardinality is not None, "Metadata is lost when going from Dataframe to Series." - assert series.name == "Brand", "Pandas Series original `name` property not retained." - + assert isinstance( + series, lux.core.series.LuxSeries + ), "Derived series is type LuxSeries." + assert df["Brand"]._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", + ], "Metadata is lost when going from Dataframe to Series." + assert ( + df.cardinality is not None + ), "Metadata is lost when going from Dataframe to Series." + assert ( + series.name == "Brand" + ), "Pandas Series original `name` property not retained." diff --git a/tests/test_parser.py b/tests/test_parser.py index 9b400554..67021583 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1,5 +1,5 @@ # 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 @@ -15,67 +15,74 @@ import pandas as pd import lux + def test_case1(): - ldf = pd.read_csv("lux/data/car.csv") - ldf.set_intent(["Horsepower"]) - assert(type(ldf._intent[0]) is lux.Clause) - assert(ldf._intent[0].attribute == "Horsepower") + ldf = pd.read_csv("lux/data/car.csv") + ldf.set_intent(["Horsepower"]) + assert type(ldf._intent[0]) is lux.Clause + assert ldf._intent[0].attribute == "Horsepower" + def test_case2(): - ldf = pd.read_csv("lux/data/car.csv") - ldf.set_intent(["Horsepower", lux.Clause("MilesPerGal", channel="x")]) - assert(type(ldf._intent[0]) is lux.Clause) - assert(ldf._intent[0].attribute == "Horsepower") - assert(type(ldf._intent[1]) is lux.Clause) - assert(ldf._intent[1].attribute == "MilesPerGal") + ldf = pd.read_csv("lux/data/car.csv") + ldf.set_intent(["Horsepower", lux.Clause("MilesPerGal", channel="x")]) + assert type(ldf._intent[0]) is lux.Clause + assert ldf._intent[0].attribute == "Horsepower" + assert type(ldf._intent[1]) is lux.Clause + assert ldf._intent[1].attribute == "MilesPerGal" + def test_case3(): - ldf = pd.read_csv("lux/data/car.csv") - ldf.set_intent(["Horsepower", "Origin=USA"]) - assert(type(ldf._intent[0]) is lux.Clause) - assert(ldf._intent[0].attribute == "Horsepower") - assert(type(ldf._intent[1]) is lux.Clause) - assert(ldf._intent[1].attribute == "Origin") - assert(ldf._intent[1].value == "USA") + ldf = pd.read_csv("lux/data/car.csv") + ldf.set_intent(["Horsepower", "Origin=USA"]) + assert type(ldf._intent[0]) is lux.Clause + assert ldf._intent[0].attribute == "Horsepower" + assert type(ldf._intent[1]) is lux.Clause + assert ldf._intent[1].attribute == "Origin" + assert ldf._intent[1].value == "USA" + def test_case4(): - ldf = pd.read_csv("lux/data/car.csv") - ldf.set_intent(["Horsepower", "Origin=USA|Japan"]) - assert(type(ldf._intent[0]) is lux.Clause) - assert(ldf._intent[0].attribute == "Horsepower") - assert(type(ldf._intent[1]) is lux.Clause) - assert(ldf._intent[1].attribute == "Origin") - assert(ldf._intent[1].value == ["USA","Japan"]) + ldf = pd.read_csv("lux/data/car.csv") + ldf.set_intent(["Horsepower", "Origin=USA|Japan"]) + assert type(ldf._intent[0]) is lux.Clause + assert ldf._intent[0].attribute == "Horsepower" + assert type(ldf._intent[1]) is lux.Clause + assert ldf._intent[1].attribute == "Origin" + assert ldf._intent[1].value == ["USA", "Japan"] + def test_case5(): - ldf = pd.read_csv("lux/data/car.csv") - ldf.set_intent([["Horsepower", "MilesPerGal", "Weight"], "Origin=USA"]) - assert(type(ldf._intent[0]) is lux.Clause) - assert(ldf._intent[0].attribute == ["Horsepower", "MilesPerGal", "Weight"]) - assert(type(ldf._intent[1]) is lux.Clause) - assert(ldf._intent[1].attribute == "Origin") - assert(ldf._intent[1].value == "USA") - - ldf.set_intent(["Horsepower|MilesPerGal|Weight", "Origin=USA"]) - assert(type(ldf._intent[0]) is lux.Clause) - assert(ldf._intent[0].attribute == ["Horsepower", "MilesPerGal", "Weight"]) - assert(type(ldf._intent[1]) is lux.Clause) - assert(ldf._intent[1].attribute == "Origin") - assert(ldf._intent[1].value == "USA") + ldf = pd.read_csv("lux/data/car.csv") + ldf.set_intent([["Horsepower", "MilesPerGal", "Weight"], "Origin=USA"]) + assert type(ldf._intent[0]) is lux.Clause + assert ldf._intent[0].attribute == ["Horsepower", "MilesPerGal", "Weight"] + assert type(ldf._intent[1]) is lux.Clause + assert ldf._intent[1].attribute == "Origin" + assert ldf._intent[1].value == "USA" + + ldf.set_intent(["Horsepower|MilesPerGal|Weight", "Origin=USA"]) + assert type(ldf._intent[0]) is lux.Clause + assert ldf._intent[0].attribute == ["Horsepower", "MilesPerGal", "Weight"] + assert type(ldf._intent[1]) is lux.Clause + assert ldf._intent[1].attribute == "Origin" + assert ldf._intent[1].value == "USA" + def test_case6(): - ldf = pd.read_csv("lux/data/car.csv") - ldf.set_intent(["Horsepower", "Origin=?"]) - ldf._repr_html_() - assert(type(ldf._intent[0]) is lux.Clause) - assert(ldf._intent[0].attribute == "Horsepower") - assert(type(ldf._intent[1]) is lux.Clause) - assert(ldf._intent[1].attribute == "Origin") - assert(ldf._intent[1].value == ["USA","Japan","Europe"]) + ldf = pd.read_csv("lux/data/car.csv") + ldf.set_intent(["Horsepower", "Origin=?"]) + ldf._repr_html_() + assert type(ldf._intent[0]) is lux.Clause + assert ldf._intent[0].attribute == "Horsepower" + assert type(ldf._intent[1]) is lux.Clause + assert ldf._intent[1].attribute == "Origin" + assert ldf._intent[1].value == ["USA", "Japan", "Europe"] + # TODO: Need to support this case -''' +""" lux.set_intent(["Horsepower","MPG","Acceleration"],"Origin") lux.set_intent("Horsepower/MPG/Acceleration", "Origin") --> [Clause(attr= ["Horsepower","MPG","Acceleration"], type= "attributeGroup")] -''' \ No newline at end of file +""" diff --git a/tests/test_performance.py b/tests/test_performance.py index 588aea55..a30b4cd2 100644 --- a/tests/test_performance.py +++ b/tests/test_performance.py @@ -1,5 +1,5 @@ # 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 @@ -16,35 +16,42 @@ import pytest import pandas as pd import time + # To run the script and see the printed result, run: # python -m pytest -s tests/test_performance.py def test_q1_performance_census(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/census.csv?raw=true' - df = pd.read_csv(url) - tic = time.perf_counter() - df._repr_html_() - toc = time.perf_counter() - delta = toc - tic - df._repr_html_() - toc2 = time.perf_counter() - delta2 = toc2 - toc - print(f"1st display Performance: {delta:0.4f} seconds") - print(f"2nd display Performance: {delta2:0.4f} seconds") - assert delta < 4.5, "The recommendations on Census dataset took a total of {delta:0.4f} seconds, longer than expected." - assert delta2 < 0.15 order_id, product_id, user_id is not visualized since it resembles an ID field." in df._message.to_html() + df = pd.read_csv( + "https://github.com/lux-org/lux-datasets/blob/master/data/instacart_sample.csv?raw=true" + ) + df._repr_html_() + assert len(df.data_type["id"]) == 3 + assert ( + "order_id, product_id, user_id is not visualized since it resembles an ID field." + in df._message.to_html() + ) + def test_check_str_id(): - df = pd.read_csv('https://github.com/lux-org/lux-datasets/blob/master/data/churn.csv?raw=true') - df._repr_html_() - assert "customerID is not visualized since it resembles an ID field." in df._message.to_html() + df = pd.read_csv( + "https://github.com/lux-org/lux-datasets/blob/master/data/churn.csv?raw=true" + ) + df._repr_html_() + assert ( + "customerID is not visualized since it resembles an ID field." + in df._message.to_html() + ) + def test_check_hpi(): - df = pd.read_csv('https://github.com/lux-org/lux-datasets/blob/master/data/hpi.csv?raw=true').head(10) + df = pd.read_csv( + "https://github.com/lux-org/lux-datasets/blob/master/data/hpi.csv?raw=true" + ).head(10) + + df.maintain_metadata() - df.maintain_metadata() + assert df.data_type_lookup == { + "HPIRank": "quantitative", + "Country": "nominal", + "SubRegion": "nominal", + "AverageLifeExpectancy": "quantitative", + "AverageWellBeing": "quantitative", + "HappyLifeYears": "quantitative", + "Footprint": "quantitative", + "InequalityOfOutcomes": "quantitative", + "InequalityAdjustedLifeExpectancy": "quantitative", + "InequalityAdjustedWellbeing": "quantitative", + "HappyPlanetIndex": "quantitative", + "GDPPerCapita": "quantitative", + "Population": "quantitative", + } - assert df.data_type_lookup == {'HPIRank': 'quantitative', - 'Country': 'nominal', - 'SubRegion': 'nominal', - 'AverageLifeExpectancy': 'quantitative', - 'AverageWellBeing': 'quantitative', - 'HappyLifeYears': 'quantitative', - 'Footprint': 'quantitative', - 'InequalityOfOutcomes': 'quantitative', - 'InequalityAdjustedLifeExpectancy': 'quantitative', - 'InequalityAdjustedWellbeing': 'quantitative', - 'HappyPlanetIndex': 'quantitative', - 'GDPPerCapita': 'quantitative', - 'Population': 'quantitative'} def test_check_airbnb(): - df = pd.read_csv('https://github.com/lux-org/lux-datasets/blob/master/data/airbnb_nyc.csv?raw=true') - df.maintain_metadata() - assert df.data_type_lookup == {'id': 'id', - 'name': 'nominal', - 'host_id': 'id', - 'host_name': 'nominal', - 'neighbourhood_group': 'nominal', - 'neighbourhood': 'nominal', - 'latitude': 'quantitative', - 'longitude': 'quantitative', - 'room_type': 'nominal', - 'price': 'quantitative', - 'minimum_nights': 'quantitative', - 'number_of_reviews': 'quantitative', - 'last_review': 'nominal', - 'reviews_per_month': 'quantitative', - 'calculated_host_listings_count': 'quantitative', - 'availability_365': 'quantitative'} + df = pd.read_csv( + "https://github.com/lux-org/lux-datasets/blob/master/data/airbnb_nyc.csv?raw=true" + ) + df.maintain_metadata() + assert df.data_type_lookup == { + "id": "id", + "name": "nominal", + "host_id": "id", + "host_name": "nominal", + "neighbourhood_group": "nominal", + "neighbourhood": "nominal", + "latitude": "quantitative", + "longitude": "quantitative", + "room_type": "nominal", + "price": "quantitative", + "minimum_nights": "quantitative", + "number_of_reviews": "quantitative", + "last_review": "nominal", + "reviews_per_month": "quantitative", + "calculated_host_listings_count": "quantitative", + "availability_365": "quantitative", + } + def test_check_college(): - df = pd.read_csv('lux/data/college.csv') - df.maintain_metadata() - assert df.data_type_lookup == {'Name': 'nominal', - 'PredominantDegree': 'nominal', - 'HighestDegree': 'nominal', - 'FundingModel': 'nominal', - 'Region': 'nominal', - 'Geography': 'nominal', - 'AdmissionRate': 'quantitative', - 'ACTMedian': 'quantitative', - 'SATAverage': 'quantitative', - 'AverageCost': 'quantitative', - 'Expenditure': 'quantitative', - 'AverageFacultySalary': 'quantitative', - 'MedianDebt': 'quantitative', - 'AverageAgeofEntry': 'quantitative', - 'MedianFamilyIncome': 'quantitative', - 'MedianEarnings': 'quantitative'} \ No newline at end of file + df = pd.read_csv("lux/data/college.csv") + df.maintain_metadata() + assert df.data_type_lookup == { + "Name": "nominal", + "PredominantDegree": "nominal", + "HighestDegree": "nominal", + "FundingModel": "nominal", + "Region": "nominal", + "Geography": "nominal", + "AdmissionRate": "quantitative", + "ACTMedian": "quantitative", + "SATAverage": "quantitative", + "AverageCost": "quantitative", + "Expenditure": "quantitative", + "AverageFacultySalary": "quantitative", + "MedianDebt": "quantitative", + "AverageAgeofEntry": "quantitative", + "MedianFamilyIncome": "quantitative", + "MedianEarnings": "quantitative", + } diff --git a/tests/test_vis.py b/tests/test_vis.py index e9ffa33c..0f6f9eec 100644 --- a/tests/test_vis.py +++ b/tests/test_vis.py @@ -1,5 +1,5 @@ # 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 @@ -17,152 +17,223 @@ import pandas as pd from lux.vis.VisList import VisList from lux.vis.Vis import Vis + + def test_vis(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true' + url = ( + "https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true" + ) df = pd.read_csv(url) - vis = Vis(["Height","SportType=Ball"],df) - assert vis.get_attr_by_attr_name("Height")[0].bin_size!=0 - assert vis.get_attr_by_attr_name("Record")[0].aggregation == 'count' - + vis = Vis(["Height", "SportType=Ball"], df) + assert vis.get_attr_by_attr_name("Height")[0].bin_size != 0 + assert vis.get_attr_by_attr_name("Record")[0].aggregation == "count" + + def test_vis_set_specs(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true' + url = ( + "https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true" + ) df = pd.read_csv(url) - vis = Vis(["Height","SportType=Ball"],df) - vis.set_intent(["Height","SportType=Ice"]) - assert vis.get_attr_by_attr_name("SportType")[0].value =="Ice" + vis = Vis(["Height", "SportType=Ball"], df) + vis.set_intent(["Height", "SportType=Ice"]) + assert vis.get_attr_by_attr_name("SportType")[0].value == "Ice" + def test_vis_collection(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true' + url = ( + "https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true" + ) df = pd.read_csv(url) - vlist = VisList(["Height","SportType=Ball","?"],df) - vis_with_year = list(filter(lambda x: x.get_attr_by_attr_name("Year")!=[],vlist))[0] - assert vis_with_year.get_attr_by_channel("x")[0].attribute=="Year" - assert len(vlist) == len(df.columns) -1 -1 #remove 1 for vis with same filter attribute and remove 1 vis with for same attribute - vlist = VisList(["Height","?"],df) - assert len(vlist) == len(df.columns) -1 #remove 1 for vis with for same attribute + vlist = VisList(["Height", "SportType=Ball", "?"], df) + vis_with_year = list( + filter(lambda x: x.get_attr_by_attr_name("Year") != [], vlist) + )[0] + assert vis_with_year.get_attr_by_channel("x")[0].attribute == "Year" + assert ( + len(vlist) == len(df.columns) - 1 - 1 + ) # remove 1 for vis with same filter attribute and remove 1 vis with for same attribute + vlist = VisList(["Height", "?"], df) + assert len(vlist) == len(df.columns) - 1 # remove 1 for vis with for same attribute + def test_vis_collection_set_intent(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true' + url = ( + "https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true" + ) df = pd.read_csv(url) - vlist = VisList(["Height","SportType=Ice","?"],df) - vlist.set_intent(["Height","SportType=Boat","?"]) - for v in vlist._collection: - filter_vspec = list(filter(lambda x: x.channel=="",v._inferred_intent))[0] - assert filter_vspec.value =="Boat" + vlist = VisList(["Height", "SportType=Ice", "?"], df) + vlist.set_intent(["Height", "SportType=Boat", "?"]) + for v in vlist._collection: + filter_vspec = list(filter(lambda x: x.channel == "", v._inferred_intent))[0] + assert filter_vspec.value == "Boat" + + def test_custom_plot_setting(): def change_color_make_transparent_add_title(chart): - chart = chart.configure_mark(color="green",opacity=0.2) + chart = chart.configure_mark(color="green", opacity=0.2) chart.title = "Test Title" return chart + df = pd.read_csv("lux/data/car.csv") df.plot_config = change_color_make_transparent_add_title df._repr_html_() - config_mark_addition = 'chart = chart.configure_mark(color="green",opacity=0.2)' - title_addition ='chart.title = "Test Title"' + config_mark_addition = 'chart = chart.configure_mark(color="green", opacity=0.2)' + title_addition = 'chart.title = "Test Title"' exported_code_str = df.recommendation["Correlation"][0].to_Altair() assert config_mark_addition in exported_code_str assert title_addition in exported_code_str + def test_remove(): df = pd.read_csv("lux/data/car.csv") - vis = Vis([lux.Clause("Horsepower"),lux.Clause("Acceleration")],df) - vis.remove_column_from_spec("Horsepower",remove_first=False) + vis = Vis([lux.Clause("Horsepower"), lux.Clause("Acceleration")], df) + vis.remove_column_from_spec("Horsepower", remove_first=False) assert vis._inferred_intent[0].attribute == "Acceleration" + + def test_remove_identity(): df = pd.read_csv("lux/data/car.csv") - vis = Vis(["Horsepower","Horsepower"],df) + vis = Vis(["Horsepower", "Horsepower"], df) vis.remove_column_from_spec("Horsepower") - assert (vis._inferred_intent == []),"Remove all instances of Horsepower" + assert vis._inferred_intent == [], "Remove all instances of Horsepower" df = pd.read_csv("lux/data/car.csv") - vis = Vis(["Horsepower","Horsepower"],df) - vis.remove_column_from_spec("Horsepower",remove_first=True) - assert (len(vis._inferred_intent)==1),"Remove only 1 instances of Horsepower" - assert (vis._inferred_intent[0].attribute=="Horsepower"),"Remove only 1 instances of Horsepower" + vis = Vis(["Horsepower", "Horsepower"], df) + vis.remove_column_from_spec("Horsepower", remove_first=True) + assert len(vis._inferred_intent) == 1, "Remove only 1 instances of Horsepower" + assert ( + vis._inferred_intent[0].attribute == "Horsepower" + ), "Remove only 1 instances of Horsepower" + + def test_refresh_collection(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - df.set_intent([lux.Clause(attribute = "Acceleration"),lux.Clause(attribute = "Horsepower")]) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + df.set_intent( + [lux.Clause(attribute="Acceleration"), lux.Clause(attribute="Horsepower")] + ) df._repr_html_() enhanceCollection = df.recommendation["Enhance"] - enhanceCollection.refresh_source(df[df["Origin"]=="USA"]) + enhanceCollection.refresh_source(df[df["Origin"] == "USA"]) + def test_vis_custom_aggregation_as_str(): df = pd.read_csv("lux/data/college.csv") import numpy as np - vis = Vis(["HighestDegree",lux.Clause("AverageCost",aggregation="max")],df) + + vis = Vis(["HighestDegree", lux.Clause("AverageCost", aggregation="max")], df) assert vis.get_attr_by_data_model("measure")[0].aggregation == "max" - assert vis.get_attr_by_data_model("measure")[0]._aggregation_name =='max' - + assert vis.get_attr_by_data_model("measure")[0]._aggregation_name == "max" + + def test_vis_custom_aggregation_as_numpy_func(): df = pd.read_csv("lux/data/college.csv") from lux.vis.Vis import Vis import numpy as np - vis = Vis(["HighestDegree",lux.Clause("AverageCost",aggregation=np.ptp)],df) + + vis = Vis(["HighestDegree", lux.Clause("AverageCost", aggregation=np.ptp)], df) assert vis.get_attr_by_data_model("measure")[0].aggregation == np.ptp - assert vis.get_attr_by_data_model("measure")[0]._aggregation_name =='ptp' + assert vis.get_attr_by_data_model("measure")[0]._aggregation_name == "ptp" + + def test_vis_collection_via_list_of_vis(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true' + url = ( + "https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true" + ) df = pd.read_csv(url) - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype from lux.vis.VisList import VisList from lux.vis.Vis import Vis + vcLst = [] - for attribute in ['Sport','Year','Height','HostRegion','SportType']: + for attribute in ["Sport", "Year", "Height", "HostRegion", "SportType"]: vis = Vis([lux.Clause("Weight"), lux.Clause(attribute)]) vcLst.append(vis) - vlist = VisList(vcLst,df) + vlist = VisList(vcLst, df) assert len(vlist) == 5 + + def test_vis_to_Altair_basic_df(): df = pd.read_csv("lux/data/car.csv") - vis = Vis(['Weight','Horsepower'],df) + vis = Vis(["Weight", "Horsepower"], df) code = vis.to_Altair() - assert "alt.Chart(df)" in code , "Unable to export to Altair" + assert "alt.Chart(df)" in code, "Unable to export to Altair" + + def test_vis_to_Altair_custom_named_df(): df = pd.read_csv("lux/data/car.csv") some_weirdly_named_df = df.dropna() - vis = Vis(['Weight','Horsepower'],some_weirdly_named_df) + vis = Vis(["Weight", "Horsepower"], some_weirdly_named_df) code = vis.to_Altair() - assert "alt.Chart(some_weirdly_named_df)" in code , "Unable to export to Altair and detect custom df name" + assert ( + "alt.Chart(some_weirdly_named_df)" in code + ), "Unable to export to Altair and detect custom df name" + + def test_vis_to_Altair_standalone(): df = pd.read_csv("lux/data/car.csv") - vis = Vis(['Weight','Horsepower'],df) + vis = Vis(["Weight", "Horsepower"], df) code = vis.to_Altair(standalone=True) - assert "chart = alt.Chart(pd.DataFrame({'Weight': {0: 3504, 1: 3693, 2: 3436, 3: 3433, 4: 3449, 5: 43" in code or "alt.Chart(pd.DataFrame({'Horsepower': {0: 130, 1: 165, 2: 150, 3: 150, 4: 140," in code + assert ( + "chart = alt.Chart(pd.DataFrame({'Weight': {0: 3504, 1: 3693, 2: 3436, 3: 3433, 4: 3449, 5: 43" + in code + or "alt.Chart(pd.DataFrame({'Horsepower': {0: 130, 1: 165, 2: 150, 3: 150, 4: 140," + in code + ) + + def test_vis_list_custom_title_override(): - url = 'https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true' + url = ( + "https://github.com/lux-org/lux-datasets/blob/master/data/olympic.csv?raw=true" + ) df = pd.read_csv(url) - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + vcLst = [] - for attribute in ['Sport','Year','Height','HostRegion','SportType']: - vis = Vis([lux.Clause("Weight"), lux.Clause(attribute)],title="overriding dummy title") + for attribute in ["Sport", "Year", "Height", "HostRegion", "SportType"]: + vis = Vis( + [lux.Clause("Weight"), lux.Clause(attribute)], + title="overriding dummy title", + ) vcLst.append(vis) - vlist = VisList(vcLst,df) - for v in vlist: - assert v.title=="overriding dummy title" + vlist = VisList(vcLst, df) + for v in vlist: + assert v.title == "overriding dummy title" + + def test_vis_set_intent(): from lux.vis.Vis import Vis + df = pd.read_csv("lux/data/car.csv") - vis = Vis(["Weight","Horsepower"],df) + vis = Vis(["Weight", "Horsepower"], df) vis._repr_html_() assert "Horsepower" in str(vis._code) - vis.intent = ["Weight","MilesPerGal"] + vis.intent = ["Weight", "MilesPerGal"] vis._repr_html_() assert "MilesPerGal" in str(vis._code) + + def test_vis_list_set_intent(): from lux.vis.VisList import VisList + df = pd.read_csv("lux/data/car.csv") - vislist = VisList(["Horsepower","?"],df) + vislist = VisList(["Horsepower", "?"], df) vislist._repr_html_() - for vis in vislist: assert vis.get_attr_by_attr_name("Horsepower")!=[] - vislist.intent = ["Weight","?"] + for vis in vislist: + assert vis.get_attr_by_attr_name("Horsepower") != [] + vislist.intent = ["Weight", "?"] vislist._repr_html_() - for vis in vislist: assert vis.get_attr_by_attr_name("Weight")!=[] + for vis in vislist: + assert vis.get_attr_by_attr_name("Weight") != [] + + def test_text_not_overridden(): from lux.vis.Vis import Vis + df = pd.read_csv("lux/data/college.csv") vis = Vis(["Region", "Geography"], df) vis._repr_html_() code = vis.to_Altair() - assert "color = \"#ff8e04\"" in code + assert 'color = "#ff8e04"' in code From 4eb2bb12dc5775c38a7d9fd5cfd76d6236214d60 Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Mon, 2 Nov 2020 18:34:39 +0800 Subject: [PATCH 034/114] fix doc failing from black format --- .../gen/lux.core.frame.LuxDataFrame.rst | 4 ++-- lux/vis/VisList.py | 21 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/doc/source/reference/gen/lux.core.frame.LuxDataFrame.rst b/doc/source/reference/gen/lux.core.frame.LuxDataFrame.rst index 66434e25..28f1110a 100644 --- a/doc/source/reference/gen/lux.core.frame.LuxDataFrame.rst +++ b/doc/source/reference/gen/lux.core.frame.LuxDataFrame.rst @@ -155,7 +155,7 @@ lux.core.frame.LuxDataFrame ~LuxDataFrame.rec_to_JSON ~LuxDataFrame.reindex ~LuxDataFrame.reindex_like - ~LuxDataFrame.removeDeletedRecs + ~LuxDataFrame.remove_deleted_recs ~LuxDataFrame.rename ~LuxDataFrame.rename_axis ~LuxDataFrame.render_widget @@ -180,6 +180,7 @@ lux.core.frame.LuxDataFrame ~LuxDataFrame.set_index ~LuxDataFrame.set_intent ~LuxDataFrame.set_intent_as_vis + ~LuxDataFrame.set_intent_on_click ~LuxDataFrame.shift ~LuxDataFrame.skew ~LuxDataFrame.slice_shift @@ -246,7 +247,6 @@ lux.core.frame.LuxDataFrame ~LuxDataFrame.axes ~LuxDataFrame.columns ~LuxDataFrame.current_vis - ~LuxDataFrame.default_display ~LuxDataFrame.dtypes ~LuxDataFrame.empty ~LuxDataFrame.exported diff --git a/lux/vis/VisList.py b/lux/vis/VisList.py index 86ccf1a1..51f4d4f7 100644 --- a/lux/vis/VisList.py +++ b/lux/vis/VisList.py @@ -63,17 +63,18 @@ def set_intent(self, intent: List[Clause]) -> None: @property def exported(self) -> VisList: """ - Get selected visualizations as exported Vis List - Notes + Get selected visualizations as exported Vis List + + Notes ----- - Convert the _selectedVisIdxs dictionary into a programmable VisList - Example _selectedVisIdxs : - {'Vis List': [0, 2]} - - Returns - ------- - VisList - return a VisList of selected visualizations. -> VisList(v1, v2...) + Convert the _selectedVisIdxs dictionary into a programmable VisList + Example _selectedVisIdxs : + {'Vis List': [0, 2]} + + Returns + ------- + VisList + return a VisList of selected visualizations. -> VisList(v1, v2...) """ if not hasattr(self, "widget"): warnings.warn( From a5eff984f06b61e1f4f6fc89a14fe99eee532649 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Mon, 2 Nov 2020 10:30:31 -0800 Subject: [PATCH 035/114] Cleaned SQL Executor Example Notebook restarted kernel and cleared output --- examples/Postgres_Executor_Example.ipynb | 148 ++--------------------- 1 file changed, 11 insertions(+), 137 deletions(-) diff --git a/examples/Postgres_Executor_Example.ipynb b/examples/Postgres_Executor_Example.ipynb index 7a3bc52a..5220e8ab 100644 --- a/examples/Postgres_Executor_Example.ipynb +++ b/examples/Postgres_Executor_Example.ipynb @@ -2,17 +2,9 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C:\\Users\\thyne\\Documents\\GitHub\\thyne-lux\n" - ] - } - ], + "outputs": [], "source": [ "cd ../" ] @@ -26,46 +18,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "73ec3be76cda474e8becf0c90299fd89", - "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": "05bb45135cd94c3995bdd7b55a186d9f", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import lux\n", "import psycopg2\n", @@ -80,109 +35,28 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'quantitative': ['milespergal',\n", - " 'displacement',\n", - " 'horsepower',\n", - " 'weight',\n", - " 'acceleration'],\n", - " 'ordinal': [],\n", - " 'nominal': ['name', 'cylinders', 'origin'],\n", - " 'temporal': ['year'],\n", - " 'id': []}" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "sql_df.data_type" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9ee7f6a218fb4724b7dd289337f480e6", - "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": "fced58dbb4cf48c28d8598a0e944ee0e", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "sql_df.set_intent([\"milespergal\"])\n", + "sql_df.set_intent([\"milespergal\", 'cylinders', Clause(attribute =\"horsepower\", filter_op=\">\", value=150)])\n", "sql_df" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "3411914c92ac42a2a7a6bb7b3ee472f6", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "LuxWidget(current_vis={'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}, 'axis': {'labelCo…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "from lux.vis.Vis import Vis\n", "from lux.vis.Vis import Clause\n", From 5e18fd80e06cfc884344a382e25ebb5c06567ac7 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Mon, 2 Nov 2020 14:34:04 -0800 Subject: [PATCH 036/114] Update custom action reference to executor Now uses executor tied to the dataframe for execution --- lux/action/custom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lux/action/custom.py b/lux/action/custom.py index 114990e7..db6ce6d9 100644 --- a/lux/action/custom.py +++ b/lux/action/custom.py @@ -38,7 +38,7 @@ def custom(ldf): recommendation["collection"] = ldf.current_vis vlist = ldf.current_vis - PandasExecutor.execute(vlist, ldf) + ldf.executor.execute(vlist, ldf) for vis in vlist: vis.score = interestingness(vis,ldf) # ldf.clear_intent() From 931122451d70092ff56ebe903b4a11ddd4d58773 Mon Sep 17 00:00:00 2001 From: thyneb19 Date: Tue, 3 Nov 2020 18:00:50 -0800 Subject: [PATCH 037/114] Updated Interestingness Tests (#133) * add black * update cars dataset and tests * Delete old dataset * Updated Interestingness Tests Updated tests to use the newly updated cars dataset * switch to local cars reference Co-authored-by: Kunal Agarwal Co-authored-by: Kunal Agarwal <32151899+westernguy2@users.noreply.github.com> Co-authored-by: 19thyneb Co-authored-by: Doris Lee --- lux/data/car.csv | 786 +++++++++++++++++----------------- tests/test_compiler.py | 3 +- tests/test_dates.py | 2 +- tests/test_interestingness.py | 18 +- tests/test_maintainence.py | 4 +- tests/test_pandas_coverage.py | 42 +- 6 files changed, 423 insertions(+), 432 deletions(-) diff --git a/lux/data/car.csv b/lux/data/car.csv index dfe35620..20065867 100644 --- a/lux/data/car.csv +++ b/lux/data/car.csv @@ -1,393 +1,393 @@ -Name,MilesPerGal,Cylinders,Displacement,Horsepower,Weight,Acceleration,Year,Origin -chevrolet chevelle malibu,18.0,8,307.0,130,3504,12.0,1970,USA -buick skylark 320,15.0,8,350.0,165,3693,11.5,1970,USA -plymouth satellite,18.0,8,318.0,150,3436,11.0,1970,USA -amc rebel sst,16.0,8,304.0,150,3433,12.0,1970,USA -ford torino,17.0,8,302.0,140,3449,10.5,1970,USA -ford galaxie 500,15.0,8,429.0,198,4341,10.0,1970,USA -chevrolet impala,14.0,8,454.0,220,4354,9.0,1970,USA -plymouth fury iii,14.0,8,440.0,215,4312,8.5,1970,USA -pontiac catalina,14.0,8,455.0,225,4425,10.0,1970,USA -amc ambassador dpl,15.0,8,390.0,190,3850,8.5,1970,USA -dodge challenger se,15.0,8,383.0,170,3563,10.0,1970,USA -plymouth 'cuda 340,14.0,8,340.0,160,3609,8.0,1970,USA -chevrolet monte carlo,15.0,8,400.0,150,3761,9.5,1970,USA -buick estate wagon (sw),14.0,8,455.0,225,3086,10.0,1970,USA -toyota corona mark ii,24.0,4,113.0,95,2372,15.0,1970,Japan -plymouth duster,22.0,6,198.0,95,2833,15.5,1970,USA -amc hornet,18.0,6,199.0,97,2774,15.5,1970,USA -ford maverick,21.0,6,200.0,85,2587,16.0,1970,USA -datsun pl510,27.0,4,97.0,88,2130,14.5,1970,Japan -volkswagen 1131 deluxe sedan,26.0,4,97.0,46,1835,20.5,1970,Europe -peugeot 504,25.0,4,110.0,87,2672,17.5,1970,Europe -audi 100 ls,24.0,4,107.0,90,2430,14.5,1970,Europe -saab 99e,25.0,4,104.0,95,2375,17.5,1970,Europe -bmw 2002,26.0,4,121.0,113,2234,12.5,1970,Europe -amc gremlin,21.0,6,199.0,90,2648,15.0,1970,USA -ford f250,10.0,8,360.0,215,4615,14.0,1970,USA -chevy c20,10.0,8,307.0,200,4376,15.0,1970,USA -dodge d200,11.0,8,318.0,210,4382,13.5,1970,USA -hi 1200d,9.0,8,304.0,193,4732,18.5,1970,USA -datsun pl510,27.0,4,97.0,88,2130,14.5,1971,Japan -chevrolet vega 2300,28.0,4,140.0,90,2264,15.5,1971,USA -toyota corona,25.0,4,113.0,95,2228,14.0,1971,Japan -amc gremlin,19.0,6,232.0,100,2634,13.0,1971,USA -plymouth satellite custom,16.0,6,225.0,105,3439,15.5,1971,USA -chevrolet chevelle malibu,17.0,6,250.0,100,3329,15.5,1971,USA -ford torino 500,19.0,6,250.0,88,3302,15.5,1971,USA -amc matador,18.0,6,232.0,100,3288,15.5,1971,USA -chevrolet impala,14.0,8,350.0,165,4209,12.0,1971,USA -pontiac catalina brougham,14.0,8,400.0,175,4464,11.5,1971,USA -ford galaxie 500,14.0,8,351.0,153,4154,13.5,1971,USA -plymouth fury iii,14.0,8,318.0,150,4096,13.0,1971,USA -dodge monaco (sw),12.0,8,383.0,180,4955,11.5,1971,USA -ford country squire (sw),13.0,8,400.0,170,4746,12.0,1971,USA -pontiac safari (sw),13.0,8,400.0,175,5140,12.0,1971,USA -amc hornet sportabout (sw),18.0,6,258.0,110,2962,13.5,1971,USA -chevrolet vega (sw),22.0,4,140.0,72,2408,19.0,1971,USA -pontiac firebird,19.0,6,250.0,100,3282,15.0,1971,USA -ford mustang,18.0,6,250.0,88,3139,14.5,1971,USA -mercury capri 2000,23.0,4,122.0,86,2220,14.0,1971,USA -opel 1900,28.0,4,116.0,90,2123,14.0,1971,Europe -peugeot 304,30.0,4,79.0,70,2074,19.5,1971,Europe -fiat 124b,30.0,4,88.0,76,2065,14.5,1971,Europe -toyota corolla 1200,31.0,4,71.0,65,1773,19.0,1971,Japan -datsun 1200,35.0,4,72.0,69,1613,18.0,1971,Japan -volkswagen model 111,27.0,4,97.0,60,1834,19.0,1971,Europe -plymouth cricket,26.0,4,91.0,70,1955,20.5,1971,USA -toyota corona hardtop,24.0,4,113.0,95,2278,15.5,1972,Japan -dodge colt hardtop,25.0,4,97.5,80,2126,17.0,1972,USA -volkswagen type 3,23.0,4,97.0,54,2254,23.5,1972,Europe -chevrolet vega,20.0,4,140.0,90,2408,19.5,1972,USA -ford pinto runabout,21.0,4,122.0,86,2226,16.5,1972,USA -chevrolet impala,13.0,8,350.0,165,4274,12.0,1972,USA -pontiac catalina,14.0,8,400.0,175,4385,12.0,1972,USA -plymouth fury iii,15.0,8,318.0,150,4135,13.5,1972,USA -ford galaxie 500,14.0,8,351.0,153,4129,13.0,1972,USA -amc ambassador sst,17.0,8,304.0,150,3672,11.5,1972,USA -mercury marquis,11.0,8,429.0,208,4633,11.0,1972,USA -buick lesabre custom,13.0,8,350.0,155,4502,13.5,1972,USA -oldsmobile delta 88 royale,12.0,8,350.0,160,4456,13.5,1972,USA -chrysler newport royal,13.0,8,400.0,190,4422,12.5,1972,USA -mazda rx2 coupe,19.0,3,70.0,97,2330,13.5,1972,Japan -amc matador (sw),15.0,8,304.0,150,3892,12.5,1972,USA -chevrolet chevelle concours (sw),13.0,8,307.0,130,4098,14.0,1972,USA -ford gran torino (sw),13.0,8,302.0,140,4294,16.0,1972,USA -plymouth satellite custom (sw),14.0,8,318.0,150,4077,14.0,1972,USA -volvo 145e (sw),18.0,4,121.0,112,2933,14.5,1972,Europe -volkswagen 411 (sw),22.0,4,121.0,76,2511,18.0,1972,Europe -peugeot 504 (sw),21.0,4,120.0,87,2979,19.5,1972,Europe -renault 12 (sw),26.0,4,96.0,69,2189,18.0,1972,Europe -ford pinto (sw),22.0,4,122.0,86,2395,16.0,1972,USA -datsun 510 (sw),28.0,4,97.0,92,2288,17.0,1972,Japan -toyouta corona mark ii (sw),23.0,4,120.0,97,2506,14.5,1972,Japan -dodge colt (sw),28.0,4,98.0,80,2164,15.0,1972,USA -toyota corolla 1600 (sw),27.0,4,97.0,88,2100,16.5,1972,Japan -buick century 350,13.0,8,350.0,175,4100,13.0,1973,USA -amc matador,14.0,8,304.0,150,3672,11.5,1973,USA -chevrolet malibu,13.0,8,350.0,145,3988,13.0,1973,USA -ford gran torino,14.0,8,302.0,137,4042,14.5,1973,USA -dodge coronet custom,15.0,8,318.0,150,3777,12.5,1973,USA -mercury marquis brougham,12.0,8,429.0,198,4952,11.5,1973,USA -chevrolet caprice classic,13.0,8,400.0,150,4464,12.0,1973,USA -ford ltd,13.0,8,351.0,158,4363,13.0,1973,USA -plymouth fury gran sedan,14.0,8,318.0,150,4237,14.5,1973,USA -chrysler new yorker brougham,13.0,8,440.0,215,4735,11.0,1973,USA -buick electra 225 custom,12.0,8,455.0,225,4951,11.0,1973,USA -amc ambassador brougham,13.0,8,360.0,175,3821,11.0,1973,USA -plymouth valiant,18.0,6,225.0,105,3121,16.5,1973,USA -chevrolet nova custom,16.0,6,250.0,100,3278,18.0,1973,USA -amc hornet,18.0,6,232.0,100,2945,16.0,1973,USA -ford maverick,18.0,6,250.0,88,3021,16.5,1973,USA -plymouth duster,23.0,6,198.0,95,2904,16.0,1973,USA -volkswagen super beetle,26.0,4,97.0,46,1950,21.0,1973,Europe -chevrolet impala,11.0,8,400.0,150,4997,14.0,1973,USA -ford country,12.0,8,400.0,167,4906,12.5,1973,USA -plymouth custom suburb,13.0,8,360.0,170,4654,13.0,1973,USA -oldsmobile vista cruiser,12.0,8,350.0,180,4499,12.5,1973,USA -amc gremlin,18.0,6,232.0,100,2789,15.0,1973,USA -toyota carina,20.0,4,97.0,88,2279,19.0,1973,Japan -chevrolet vega,21.0,4,140.0,72,2401,19.5,1973,USA -datsun 610,22.0,4,108.0,94,2379,16.5,1973,Japan -maxda rx3,18.0,3,70.0,90,2124,13.5,1973,Japan -ford pinto,19.0,4,122.0,85,2310,18.5,1973,USA -mercury capri v6,21.0,6,155.0,107,2472,14.0,1973,USA -fiat 124 sport coupe,26.0,4,98.0,90,2265,15.5,1973,Europe -chevrolet monte carlo s,15.0,8,350.0,145,4082,13.0,1973,USA -pontiac grand prix,16.0,8,400.0,230,4278,9.5,1973,USA -fiat 128,29.0,4,68.0,49,1867,19.5,1973,Europe -opel manta,24.0,4,116.0,75,2158,15.5,1973,Europe -audi 100ls,20.0,4,114.0,91,2582,14.0,1973,Europe -volvo 144ea,19.0,4,121.0,112,2868,15.5,1973,Europe -dodge dart custom,15.0,8,318.0,150,3399,11.0,1973,USA -saab 99le,24.0,4,121.0,110,2660,14.0,1973,Europe -toyota mark ii,20.0,6,156.0,122,2807,13.5,1973,Japan -oldsmobile omega,11.0,8,350.0,180,3664,11.0,1973,USA -plymouth duster,20.0,6,198.0,95,3102,16.5,1974,USA -amc hornet,19.0,6,232.0,100,2901,16.0,1974,USA -chevrolet nova,15.0,6,250.0,100,3336,17.0,1974,USA -datsun b210,31.0,4,79.0,67,1950,19.0,1974,Japan -ford pinto,26.0,4,122.0,80,2451,16.5,1974,USA -toyota corolla 1200,32.0,4,71.0,65,1836,21.0,1974,Japan -chevrolet vega,25.0,4,140.0,75,2542,17.0,1974,USA -chevrolet chevelle malibu classic,16.0,6,250.0,100,3781,17.0,1974,USA -amc matador,16.0,6,258.0,110,3632,18.0,1974,USA -plymouth satellite sebring,18.0,6,225.0,105,3613,16.5,1974,USA -ford gran torino,16.0,8,302.0,140,4141,14.0,1974,USA -buick century luxus (sw),13.0,8,350.0,150,4699,14.5,1974,USA -dodge coronet custom (sw),14.0,8,318.0,150,4457,13.5,1974,USA -ford gran torino (sw),14.0,8,302.0,140,4638,16.0,1974,USA -amc matador (sw),14.0,8,304.0,150,4257,15.5,1974,USA -audi fox,29.0,4,98.0,83,2219,16.5,1974,Europe -volkswagen dasher,26.0,4,79.0,67,1963,15.5,1974,Europe -opel manta,26.0,4,97.0,78,2300,14.5,1974,Europe -toyota corona,31.0,4,76.0,52,1649,16.5,1974,Japan -datsun 710,32.0,4,83.0,61,2003,19.0,1974,Japan -dodge colt,28.0,4,90.0,75,2125,14.5,1974,USA -fiat 128,24.0,4,90.0,75,2108,15.5,1974,Europe -fiat 124 tc,26.0,4,116.0,75,2246,14.0,1974,Europe -honda civic,24.0,4,120.0,97,2489,15.0,1974,Japan -subaru,26.0,4,108.0,93,2391,15.5,1974,Japan -fiat x1.9,31.0,4,79.0,67,2000,16.0,1974,Europe -plymouth valiant custom,19.0,6,225.0,95,3264,16.0,1975,USA -chevrolet nova,18.0,6,250.0,105,3459,16.0,1975,USA -mercury monarch,15.0,6,250.0,72,3432,21.0,1975,USA -ford maverick,15.0,6,250.0,72,3158,19.5,1975,USA -pontiac catalina,16.0,8,400.0,170,4668,11.5,1975,USA -chevrolet bel air,15.0,8,350.0,145,4440,14.0,1975,USA -plymouth grand fury,16.0,8,318.0,150,4498,14.5,1975,USA -ford ltd,14.0,8,351.0,148,4657,13.5,1975,USA -buick century,17.0,6,231.0,110,3907,21.0,1975,USA -chevroelt chevelle malibu,16.0,6,250.0,105,3897,18.5,1975,USA -amc matador,15.0,6,258.0,110,3730,19.0,1975,USA -plymouth fury,18.0,6,225.0,95,3785,19.0,1975,USA -buick skyhawk,21.0,6,231.0,110,3039,15.0,1975,USA -chevrolet monza 2+2,20.0,8,262.0,110,3221,13.5,1975,USA -ford mustang ii,13.0,8,302.0,129,3169,12.0,1975,USA -toyota corolla,29.0,4,97.0,75,2171,16.0,1975,Japan -ford pinto,23.0,4,140.0,83,2639,17.0,1975,USA -amc gremlin,20.0,6,232.0,100,2914,16.0,1975,USA -pontiac astro,23.0,4,140.0,78,2592,18.5,1975,USA -toyota corona,24.0,4,134.0,96,2702,13.5,1975,Japan -volkswagen dasher,25.0,4,90.0,71,2223,16.5,1975,Europe -datsun 710,24.0,4,119.0,97,2545,17.0,1975,Japan -ford pinto,18.0,6,171.0,97,2984,14.5,1975,USA -volkswagen rabbit,29.0,4,90.0,70,1937,14.0,1975,Europe -amc pacer,19.0,6,232.0,90,3211,17.0,1975,USA -audi 100ls,23.0,4,115.0,95,2694,15.0,1975,Europe -peugeot 504,23.0,4,120.0,88,2957,17.0,1975,Europe -volvo 244dl,22.0,4,121.0,98,2945,14.5,1975,Europe -saab 99le,25.0,4,121.0,115,2671,13.5,1975,Europe -honda civic cvcc,33.0,4,91.0,53,1795,17.5,1975,Japan -fiat 131,28.0,4,107.0,86,2464,15.5,1976,Europe -opel 1900,25.0,4,116.0,81,2220,16.9,1976,Europe -capri ii,25.0,4,140.0,92,2572,14.9,1976,USA -dodge colt,26.0,4,98.0,79,2255,17.7,1976,USA -renault 12tl,27.0,4,101.0,83,2202,15.3,1976,Europe -chevrolet chevelle malibu classic,17.5,8,305.0,140,4215,13.0,1976,USA -dodge coronet brougham,16.0,8,318.0,150,4190,13.0,1976,USA -amc matador,15.5,8,304.0,120,3962,13.9,1976,USA -ford gran torino,14.5,8,351.0,152,4215,12.8,1976,USA -plymouth valiant,22.0,6,225.0,100,3233,15.4,1976,USA -chevrolet nova,22.0,6,250.0,105,3353,14.5,1976,USA -ford maverick,24.0,6,200.0,81,3012,17.6,1976,USA -amc hornet,22.5,6,232.0,90,3085,17.6,1976,USA -chevrolet chevette,29.0,4,85.0,52,2035,22.2,1976,USA -chevrolet woody,24.5,4,98.0,60,2164,22.1,1976,USA -vw rabbit,29.0,4,90.0,70,1937,14.2,1976,Europe -honda civic,33.0,4,91.0,53,1795,17.4,1976,Japan -dodge aspen se,20.0,6,225.0,100,3651,17.7,1976,USA -ford granada ghia,18.0,6,250.0,78,3574,21.0,1976,USA -pontiac ventura sj,18.5,6,250.0,110,3645,16.2,1976,USA -amc pacer d/l,17.5,6,258.0,95,3193,17.8,1976,USA -volkswagen rabbit,29.5,4,97.0,71,1825,12.2,1976,Europe -datsun b-210,32.0,4,85.0,70,1990,17.0,1976,Japan -toyota corolla,28.0,4,97.0,75,2155,16.4,1976,Japan -ford pinto,26.5,4,140.0,72,2565,13.6,1976,USA -volvo 245,20.0,4,130.0,102,3150,15.7,1976,Europe -plymouth volare premier v8,13.0,8,318.0,150,3940,13.2,1976,USA -peugeot 504,19.0,4,120.0,88,3270,21.9,1976,Europe -toyota mark ii,19.0,6,156.0,108,2930,15.5,1976,Japan -mercedes-benz 280s,16.5,6,168.0,120,3820,16.7,1976,Europe -cadillac seville,16.5,8,350.0,180,4380,12.1,1976,USA -chevy c10,13.0,8,350.0,145,4055,12.0,1976,USA -ford f108,13.0,8,302.0,130,3870,15.0,1976,USA -dodge d100,13.0,8,318.0,150,3755,14.0,1976,USA -honda Accelerationord cvcc,31.5,4,98.0,68,2045,18.5,1977,Japan -buick opel isuzu deluxe,30.0,4,111.0,80,2155,14.8,1977,USA -renault 5 gtl,36.0,4,79.0,58,1825,18.6,1977,Europe -plymouth arrow gs,25.5,4,122.0,96,2300,15.5,1977,USA -datsun f-10 hatchback,33.5,4,85.0,70,1945,16.8,1977,Japan -chevrolet caprice classic,17.5,8,305.0,145,3880,12.5,1977,USA -oldsmobile cutlass supreme,17.0,8,260.0,110,4060,19.0,1977,USA -dodge monaco brougham,15.5,8,318.0,145,4140,13.7,1977,USA -mercury cougar brougham,15.0,8,302.0,130,4295,14.9,1977,USA -chevrolet concours,17.5,6,250.0,110,3520,16.4,1977,USA -buick skylark,20.5,6,231.0,105,3425,16.9,1977,USA -plymouth volare custom,19.0,6,225.0,100,3630,17.7,1977,USA -ford granada,18.5,6,250.0,98,3525,19.0,1977,USA -pontiac grand prix lj,16.0,8,400.0,180,4220,11.1,1977,USA -chevrolet monte carlo landau,15.5,8,350.0,170,4165,11.4,1977,USA -chrysler cordoba,15.5,8,400.0,190,4325,12.2,1977,USA -ford thunderbird,16.0,8,351.0,149,4335,14.5,1977,USA -volkswagen rabbit custom,29.0,4,97.0,78,1940,14.5,1977,Europe -pontiac sunbird coupe,24.5,4,151.0,88,2740,16.0,1977,USA -toyota corolla liftback,26.0,4,97.0,75,2265,18.2,1977,Japan -ford mustang ii 2+2,25.5,4,140.0,89,2755,15.8,1977,USA -chevrolet chevette,30.5,4,98.0,63,2051,17.0,1977,USA -dodge colt m/m,33.5,4,98.0,83,2075,15.9,1977,USA -subaru dl,30.0,4,97.0,67,1985,16.4,1977,Japan -volkswagen dasher,30.5,4,97.0,78,2190,14.1,1977,Europe -datsun 810,22.0,6,146.0,97,2815,14.5,1977,Japan -bmw 320i,21.5,4,121.0,110,2600,12.8,1977,Europe -mazda rx-4,21.5,3,80.0,110,2720,13.5,1977,Japan -volkswagen rabbit custom diesel,43.1,4,90.0,48,1985,21.5,1978,Europe -ford fiesta,36.1,4,98.0,66,1800,14.4,1978,USA -mazda glc deluxe,32.8,4,78.0,52,1985,19.4,1978,Japan -datsun b210 gx,39.4,4,85.0,70,2070,18.6,1978,Japan -honda civic cvcc,36.1,4,91.0,60,1800,16.4,1978,Japan -oldsmobile cutlass salon brougham,19.9,8,260.0,110,3365,15.5,1978,USA -dodge diplomat,19.4,8,318.0,140,3735,13.2,1978,USA -mercury monarch ghia,20.2,8,302.0,139,3570,12.8,1978,USA -pontiac phoenix lj,19.2,6,231.0,105,3535,19.2,1978,USA -chevrolet malibu,20.5,6,200.0,95,3155,18.2,1978,USA -ford fairmont (auto),20.2,6,200.0,85,2965,15.8,1978,USA -ford fairmont (man),25.1,4,140.0,88,2720,15.4,1978,USA -plymouth volare,20.5,6,225.0,100,3430,17.2,1978,USA -amc concord,19.4,6,232.0,90,3210,17.2,1978,USA -buick century special,20.6,6,231.0,105,3380,15.8,1978,USA -mercury zephyr,20.8,6,200.0,85,3070,16.7,1978,USA -dodge aspen,18.6,6,225.0,110,3620,18.7,1978,USA -amc concord d/l,18.1,6,258.0,120,3410,15.1,1978,USA -chevrolet monte carlo landau,19.2,8,305.0,145,3425,13.2,1978,USA -buick regal sport coupe (turbo),17.7,6,231.0,165,3445,13.4,1978,USA -ford futura,18.1,8,302.0,139,3205,11.2,1978,USA -dodge magnum xe,17.5,8,318.0,140,4080,13.7,1978,USA -chevrolet chevette,30.0,4,98.0,68,2155,16.5,1978,USA -toyota corona,27.5,4,134.0,95,2560,14.2,1978,Japan -datsun 510,27.2,4,119.0,97,2300,14.7,1978,Japan -dodge omni,30.9,4,105.0,75,2230,14.5,1978,USA -toyota celica gt liftback,21.1,4,134.0,95,2515,14.8,1978,Japan -plymouth sapporo,23.2,4,156.0,105,2745,16.7,1978,USA -oldsmobile starfire sx,23.8,4,151.0,85,2855,17.6,1978,USA -datsun 200-sx,23.9,4,119.0,97,2405,14.9,1978,Japan -audi 5000,20.3,5,131.0,103,2830,15.9,1978,Europe -volvo 264gl,17.0,6,163.0,125,3140,13.6,1978,Europe -saab 99gle,21.6,4,121.0,115,2795,15.7,1978,Europe -peugeot 604sl,16.2,6,163.0,133,3410,15.8,1978,Europe -volkswagen scirocco,31.5,4,89.0,71,1990,14.9,1978,Europe -honda Accelerationord lx,29.5,4,98.0,68,2135,16.6,1978,Japan -pontiac lemans v6,21.5,6,231.0,115,3245,15.4,1979,USA -mercury zephyr 6,19.8,6,200.0,85,2990,18.2,1979,USA -ford fairmont 4,22.3,4,140.0,88,2890,17.3,1979,USA -amc concord dl 6,20.2,6,232.0,90,3265,18.2,1979,USA -dodge aspen 6,20.6,6,225.0,110,3360,16.6,1979,USA -chevrolet caprice classic,17.0,8,305.0,130,3840,15.4,1979,USA -ford ltd landau,17.6,8,302.0,129,3725,13.4,1979,USA -mercury grand marquis,16.5,8,351.0,138,3955,13.2,1979,USA -dodge st. regis,18.2,8,318.0,135,3830,15.2,1979,USA -buick estate wagon (sw),16.9,8,350.0,155,4360,14.9,1979,USA -ford country squire (sw),15.5,8,351.0,142,4054,14.3,1979,USA -chevrolet malibu classic (sw),19.2,8,267.0,125,3605,15.0,1979,USA -chrysler lebaron town @ country (sw),18.5,8,360.0,150,3940,13.0,1979,USA -vw rabbit custom,31.9,4,89.0,71,1925,14.0,1979,Europe -maxda glc deluxe,34.1,4,86.0,65,1975,15.2,1979,Japan -dodge colt hatchback custom,35.7,4,98.0,80,1915,14.4,1979,USA -amc spirit dl,27.4,4,121.0,80,2670,15.0,1979,USA -mercedes benz 300d,25.4,5,183.0,77,3530,20.1,1979,Europe -cadillac eldorado,23.0,8,350.0,125,3900,17.4,1979,USA -peugeot 504,27.2,4,141.0,71,3190,24.8,1979,Europe -oldsmobile cutlass salon brougham,23.9,8,260.0,90,3420,22.2,1979,USA -plymouth horizon,34.2,4,105.0,70,2200,13.2,1979,USA -plymouth horizon tc3,34.5,4,105.0,70,2150,14.9,1979,USA -datsun 210,31.8,4,85.0,65,2020,19.2,1979,Japan -fiat strada custom,37.3,4,91.0,69,2130,14.7,1979,Europe -buick skylark limited,28.4,4,151.0,90,2670,16.0,1979,USA -chevrolet citation,28.8,6,173.0,115,2595,11.3,1979,USA -oldsmobile omega brougham,26.8,6,173.0,115,2700,12.9,1979,USA -pontiac phoenix,33.5,4,151.0,90,2556,13.2,1979,USA -vw rabbit,41.5,4,98.0,76,2144,14.7,1980,Europe -toyota corolla tercel,38.1,4,89.0,60,1968,18.8,1980,Japan -chevrolet chevette,32.1,4,98.0,70,2120,15.5,1980,USA -datsun 310,37.2,4,86.0,65,2019,16.4,1980,Japan -chevrolet citation,28.0,4,151.0,90,2678,16.5,1980,USA -ford fairmont,26.4,4,140.0,88,2870,18.1,1980,USA -amc concord,24.3,4,151.0,90,3003,20.1,1980,USA -dodge aspen,19.1,6,225.0,90,3381,18.7,1980,USA -audi 4000,34.3,4,97.0,78,2188,15.8,1980,Europe -toyota corona liftback,29.8,4,134.0,90,2711,15.5,1980,Japan -mazda 626,31.3,4,120.0,75,2542,17.5,1980,Japan -datsun 510 hatchback,37.0,4,119.0,92,2434,15.0,1980,Japan -toyota corolla,32.2,4,108.0,75,2265,15.2,1980,Japan -mazda glc,46.6,4,86.0,65,2110,17.9,1980,Japan -dodge colt,27.9,4,156.0,105,2800,14.4,1980,USA -datsun 210,40.8,4,85.0,65,2110,19.2,1980,Japan -vw rabbit c (diesel),44.3,4,90.0,48,2085,21.7,1980,Europe -vw dasher (diesel),43.4,4,90.0,48,2335,23.7,1980,Europe -audi 5000s (diesel),36.4,5,121.0,67,2950,19.9,1980,Europe -mercedes-benz 240d,30.0,4,146.0,67,3250,21.8,1980,Europe -honda civic 1500 gl,44.6,4,91.0,67,1850,13.8,1980,Japan -subaru dl,33.8,4,97.0,67,2145,18.0,1980,Japan -vokswagen rabbit,29.8,4,89.0,62,1845,15.3,1980,Europe -datsun 280-zx,32.7,6,168.0,132,2910,11.4,1980,Japan -mazda rx-7 gs,23.7,3,70.0,100,2420,12.5,1980,Japan -triumph tr7 coupe,35.0,4,122.0,88,2500,15.1,1980,Europe -honda Accelerationord,32.4,4,107.0,72,2290,17.0,1980,Japan -plymouth reliant,27.2,4,135.0,84,2490,15.7,1982,USA -buick skylark,26.6,4,151.0,84,2635,16.4,1982,USA -dodge aries wagon (sw),25.8,4,156.0,92,2620,14.4,1982,USA -chevrolet citation,23.5,6,173.0,110,2725,12.6,1982,USA -plymouth reliant,30.0,4,135.0,84,2385,12.9,1982,USA -toyota starlet,39.1,4,79.0,58,1755,16.9,1982,Japan -plymouth champ,39.0,4,86.0,64,1875,16.4,1982,USA -honda civic 1300,35.1,4,81.0,60,1760,16.1,1982,Japan -subaru,32.3,4,97.0,67,2065,17.8,1982,Japan -datsun 210,37.0,4,85.0,65,1975,19.4,1982,Japan -toyota tercel,37.7,4,89.0,62,2050,17.3,1982,Japan -mazda glc 4,34.1,4,91.0,68,1985,16.0,1982,Japan -plymouth horizon 4,34.7,4,105.0,63,2215,14.9,1982,USA -ford escort 4w,34.4,4,98.0,65,2045,16.2,1982,USA -ford escort 2h,29.9,4,98.0,65,2380,20.7,1982,USA -volkswagen jetta,33.0,4,105.0,74,2190,14.2,1982,Europe -honda prelude,33.7,4,107.0,75,2210,14.4,1982,Japan -toyota corolla,32.4,4,108.0,75,2350,16.8,1982,Japan -datsun 200sx,32.9,4,119.0,100,2615,14.8,1982,Japan -mazda 626,31.6,4,120.0,74,2635,18.3,1982,Japan -peugeot 505s turbo diesel,28.1,4,141.0,80,3230,20.4,1982,Europe -volvo diesel,30.7,6,145.0,76,3160,19.6,1982,Europe -toyota cressida,25.4,6,168.0,116,2900,12.6,1982,Japan -datsun 810 maxima,24.2,6,146.0,120,2930,13.8,1982,Japan -buick century,22.4,6,231.0,110,3415,15.8,1982,USA -oldsmobile cutlass ls,26.6,8,350.0,105,3725,19.0,1982,USA -ford granada gl,20.2,6,200.0,88,3060,17.1,1982,USA -chrysler lebaron salon,17.6,6,225.0,85,3465,16.6,1982,USA -chevrolet cavalier,28.0,4,112.0,88,2605,19.6,1982,USA -chevrolet cavalier wagon,27.0,4,112.0,88,2640,18.6,1982,USA -chevrolet cavalier 2-door,34.0,4,112.0,88,2395,18.0,1982,USA -pontiac j2000 se hatchback,31.0,4,112.0,85,2575,16.2,1982,USA -dodge aries se,29.0,4,135.0,84,2525,16.0,1982,USA -pontiac phoenix,27.0,4,151.0,90,2735,18.0,1982,USA -ford fairmont futura,24.0,4,140.0,92,2865,16.4,1982,USA -volkswagen rabbit l,36.0,4,105.0,74,1980,15.3,1982,Europe -mazda glc custom l,37.0,4,91.0,68,2025,18.2,1982,Japan -mazda glc custom,31.0,4,91.0,68,1970,17.6,1982,Japan -plymouth horizon miser,38.0,4,105.0,63,2125,14.7,1982,USA -mercury lynx l,36.0,4,98.0,70,2125,17.3,1982,USA -nissan stanza xe,36.0,4,120.0,88,2160,14.5,1982,Japan -honda Accelerationord,36.0,4,107.0,75,2205,14.5,1982,Japan -toyota corolla,34.0,4,108.0,70,2245,16.9,1982,Japan -honda civic,38.0,4,91.0,67,1965,15.0,1982,Japan -honda civic (auto),32.0,4,91.0,67,1965,15.7,1982,Japan -datsun 310 gx,38.0,4,91.0,67,1995,16.2,1982,Japan -buick century limited,25.0,6,181.0,110,2945,16.4,1982,USA -oldsmobile cutlass ciera (diesel),38.0,6,262.0,85,3015,17.0,1982,USA -chrysler lebaron medallion,26.0,4,156.0,92,2585,14.5,1982,USA -ford granada l,22.0,6,232.0,112,2835,14.7,1982,USA -toyota celica gt,32.0,4,144.0,96,2665,13.9,1982,Japan -dodge charger 2.2,36.0,4,135.0,84,2370,13.0,1982,USA -chevrolet camaro,27.0,4,151.0,90,2950,17.3,1982,USA -ford mustang gl,27.0,4,140.0,86,2790,15.6,1982,USA -vw pickup,44.0,4,97.0,52,2130,24.6,1982,Europe -dodge rampage,32.0,4,135.0,84,2295,11.6,1982,USA -ford ranger,28.0,4,120.0,79,2625,18.6,1982,USA -chevy s-10,31.0,4,119.0,82,2720,19.4,1982,USA +Name,MilesPerGal,Cylinders,Displacement,Horsepower,Weight,Acceleration,Year,Origin,Brand +chevrolet chevelle malibu,18,8,307,130,3504,12,1970,USA,chevrolet +buick skylark 320,15,8,350,165,3693,11.5,1970,USA,buick +plymouth satellite,18,8,318,150,3436,11,1970,USA,plymouth +amc rebel sst,16,8,304,150,3433,12,1970,USA,amc +ford torino,17,8,302,140,3449,10.5,1970,USA,ford +ford galaxie 500,15,8,429,198,4341,10,1970,USA,ford +chevrolet impala,14,8,454,220,4354,9,1970,USA,chevrolet +plymouth fury iii,14,8,440,215,4312,8.5,1970,USA,plymouth +pontiac catalina,14,8,455,225,4425,10,1970,USA,pontiac +amc ambassador dpl,15,8,390,190,3850,8.5,1970,USA,amc +dodge challenger se,15,8,383,170,3563,10,1970,USA,dodge +plymouth 'cuda 340,14,8,340,160,3609,8,1970,USA,plymouth +chevrolet monte carlo,15,8,400,150,3761,9.5,1970,USA,chevrolet +buick estate wagon (sw),14,8,455,225,3086,10,1970,USA,buick +toyota corona mark ii,24,4,113,95,2372,15,1970,Japan,toyota +plymouth duster,22,6,198,95,2833,15.5,1970,USA,plymouth +amc hornet,18,6,199,97,2774,15.5,1970,USA,amc +ford maverick,21,6,200,85,2587,16,1970,USA,ford +datsun pl510,27,4,97,88,2130,14.5,1970,Japan,datsun +volkswagen 1131 deluxe sedan,26,4,97,46,1835,20.5,1970,Europe,volkswagen +peugeot 504,25,4,110,87,2672,17.5,1970,Europe,peugeot +audi 100 ls,24,4,107,90,2430,14.5,1970,Europe,audi +saab 99e,25,4,104,95,2375,17.5,1970,Europe,saab +bmw 2002,26,4,121,113,2234,12.5,1970,Europe,bmw +amc gremlin,21,6,199,90,2648,15,1970,USA,amc +ford f250,10,8,360,215,4615,14,1970,USA,ford +chevy c20,10,8,307,200,4376,15,1970,USA,chevrolet +dodge d200,11,8,318,210,4382,13.5,1970,USA,dodge +hi 1200d,9,8,304,193,4732,18.5,1970,USA,hi +datsun pl510,27,4,97,88,2130,14.5,1971,Japan,datsun +chevrolet vega 2300,28,4,140,90,2264,15.5,1971,USA,chevrolet +toyota corona,25,4,113,95,2228,14,1971,Japan,toyota +amc gremlin,19,6,232,100,2634,13,1971,USA,amc +plymouth satellite custom,16,6,225,105,3439,15.5,1971,USA,plymouth +chevrolet chevelle malibu,17,6,250,100,3329,15.5,1971,USA,chevrolet +ford torino 500,19,6,250,88,3302,15.5,1971,USA,ford +amc matador,18,6,232,100,3288,15.5,1971,USA,amc +chevrolet impala,14,8,350,165,4209,12,1971,USA,chevrolet +pontiac catalina brougham,14,8,400,175,4464,11.5,1971,USA,pontiac +ford galaxie 500,14,8,351,153,4154,13.5,1971,USA,ford +plymouth fury iii,14,8,318,150,4096,13,1971,USA,plymouth +dodge monaco (sw),12,8,383,180,4955,11.5,1971,USA,dodge +ford country squire (sw),13,8,400,170,4746,12,1971,USA,ford +pontiac safari (sw),13,8,400,175,5140,12,1971,USA,pontiac +amc hornet sportabout (sw),18,6,258,110,2962,13.5,1971,USA,amc +chevrolet vega (sw),22,4,140,72,2408,19,1971,USA,chevrolet +pontiac firebird,19,6,250,100,3282,15,1971,USA,pontiac +ford mustang,18,6,250,88,3139,14.5,1971,USA,ford +mercury capri 2000,23,4,122,86,2220,14,1971,USA,mercury +opel 1900,28,4,116,90,2123,14,1971,Europe,opel +peugeot 304,30,4,79,70,2074,19.5,1971,Europe,peugeot +fiat 124b,30,4,88,76,2065,14.5,1971,Europe,fiat +toyota corolla 1200,31,4,71,65,1773,19,1971,Japan,toyota +datsun 1200,35,4,72,69,1613,18,1971,Japan,datsun +volkswagen model 111,27,4,97,60,1834,19,1971,Europe,volkswagen +plymouth cricket,26,4,91,70,1955,20.5,1971,USA,plymouth +toyota corona hardtop,24,4,113,95,2278,15.5,1972,Japan,toyota +dodge colt hardtop,25,4,97.5,80,2126,17,1972,USA,dodge +volkswagen type 3,23,4,97,54,2254,23.5,1972,Europe,volkswagen +chevrolet vega,20,4,140,90,2408,19.5,1972,USA,chevrolet +ford pinto runabout,21,4,122,86,2226,16.5,1972,USA,ford +chevrolet impala,13,8,350,165,4274,12,1972,USA,chevrolet +pontiac catalina,14,8,400,175,4385,12,1972,USA,pontiac +plymouth fury iii,15,8,318,150,4135,13.5,1972,USA,plymouth +ford galaxie 500,14,8,351,153,4129,13,1972,USA,ford +amc ambassador sst,17,8,304,150,3672,11.5,1972,USA,amc +mercury marquis,11,8,429,208,4633,11,1972,USA,mercury +buick lesabre custom,13,8,350,155,4502,13.5,1972,USA,buick +oldsmobile delta 88 royale,12,8,350,160,4456,13.5,1972,USA,oldsmobile +chrysler newport royal,13,8,400,190,4422,12.5,1972,USA,chrysler +mazda rx2 coupe,19,3,70,97,2330,13.5,1972,Japan,mazda +amc matador (sw),15,8,304,150,3892,12.5,1972,USA,amc +chevrolet chevelle concours (sw),13,8,307,130,4098,14,1972,USA,chevrolet +ford gran torino (sw),13,8,302,140,4294,16,1972,USA,ford +plymouth satellite custom (sw),14,8,318,150,4077,14,1972,USA,plymouth +volvo 145e (sw),18,4,121,112,2933,14.5,1972,Europe,volvo +volkswagen 411 (sw),22,4,121,76,2511,18,1972,Europe,volkswagen +peugeot 504 (sw),21,4,120,87,2979,19.5,1972,Europe,peugeot +renault 12 (sw),26,4,96,69,2189,18,1972,Europe,renault +ford pinto (sw),22,4,122,86,2395,16,1972,USA,ford +datsun 510 (sw),28,4,97,92,2288,17,1972,Japan,datsun +toyouta corona mark ii (sw),23,4,120,97,2506,14.5,1972,Japan,toyota +dodge colt (sw),28,4,98,80,2164,15,1972,USA,dodge +toyota corolla 1600 (sw),27,4,97,88,2100,16.5,1972,Japan,toyota +buick century 350,13,8,350,175,4100,13,1973,USA,buick +amc matador,14,8,304,150,3672,11.5,1973,USA,amc +chevrolet malibu,13,8,350,145,3988,13,1973,USA,chevrolet +ford gran torino,14,8,302,137,4042,14.5,1973,USA,ford +dodge coronet custom,15,8,318,150,3777,12.5,1973,USA,dodge +mercury marquis brougham,12,8,429,198,4952,11.5,1973,USA,mercury +chevrolet caprice classic,13,8,400,150,4464,12,1973,USA,chevrolet +ford ltd,13,8,351,158,4363,13,1973,USA,ford +plymouth fury gran sedan,14,8,318,150,4237,14.5,1973,USA,plymouth +chrysler new yorker brougham,13,8,440,215,4735,11,1973,USA,chrysler +buick electra 225 custom,12,8,455,225,4951,11,1973,USA,buick +amc ambassador brougham,13,8,360,175,3821,11,1973,USA,amc +plymouth valiant,18,6,225,105,3121,16.5,1973,USA,plymouth +chevrolet nova custom,16,6,250,100,3278,18,1973,USA,chevrolet +amc hornet,18,6,232,100,2945,16,1973,USA,amc +ford maverick,18,6,250,88,3021,16.5,1973,USA,ford +plymouth duster,23,6,198,95,2904,16,1973,USA,plymouth +volkswagen super beetle,26,4,97,46,1950,21,1973,Europe,volkswagen +chevrolet impala,11,8,400,150,4997,14,1973,USA,chevrolet +ford country,12,8,400,167,4906,12.5,1973,USA,ford +plymouth custom suburb,13,8,360,170,4654,13,1973,USA,plymouth +oldsmobile vista cruiser,12,8,350,180,4499,12.5,1973,USA,oldsmobile +amc gremlin,18,6,232,100,2789,15,1973,USA,amc +toyota carina,20,4,97,88,2279,19,1973,Japan,toyota +chevrolet vega,21,4,140,72,2401,19.5,1973,USA,chevrolet +datsun 610,22,4,108,94,2379,16.5,1973,Japan,datsun +maxda rx3,18,3,70,90,2124,13.5,1973,Japan,mazda +ford pinto,19,4,122,85,2310,18.5,1973,USA,ford +mercury capri v6,21,6,155,107,2472,14,1973,USA,mercury +fiat 124 sport coupe,26,4,98,90,2265,15.5,1973,Europe,fiat +chevrolet monte carlo s,15,8,350,145,4082,13,1973,USA,chevrolet +pontiac grand prix,16,8,400,230,4278,9.5,1973,USA,pontiac +fiat 128,29,4,68,49,1867,19.5,1973,Europe,fiat +opel manta,24,4,116,75,2158,15.5,1973,Europe,opel +audi 100ls,20,4,114,91,2582,14,1973,Europe,audi +volvo 144ea,19,4,121,112,2868,15.5,1973,Europe,volvo +dodge dart custom,15,8,318,150,3399,11,1973,USA,dodge +saab 99le,24,4,121,110,2660,14,1973,Europe,saab +toyota mark ii,20,6,156,122,2807,13.5,1973,Japan,toyota +oldsmobile omega,11,8,350,180,3664,11,1973,USA,oldsmobile +plymouth duster,20,6,198,95,3102,16.5,1974,USA,plymouth +amc hornet,19,6,232,100,2901,16,1974,USA,amc +chevrolet nova,15,6,250,100,3336,17,1974,USA,chevrolet +datsun b210,31,4,79,67,1950,19,1974,Japan,datsun +ford pinto,26,4,122,80,2451,16.5,1974,USA,ford +toyota corolla 1200,32,4,71,65,1836,21,1974,Japan,toyota +chevrolet vega,25,4,140,75,2542,17,1974,USA,chevrolet +chevrolet chevelle malibu classic,16,6,250,100,3781,17,1974,USA,chevrolet +amc matador,16,6,258,110,3632,18,1974,USA,amc +plymouth satellite sebring,18,6,225,105,3613,16.5,1974,USA,plymouth +ford gran torino,16,8,302,140,4141,14,1974,USA,ford +buick century luxus (sw),13,8,350,150,4699,14.5,1974,USA,buick +dodge coronet custom (sw),14,8,318,150,4457,13.5,1974,USA,dodge +ford gran torino (sw),14,8,302,140,4638,16,1974,USA,ford +amc matador (sw),14,8,304,150,4257,15.5,1974,USA,amc +audi fox,29,4,98,83,2219,16.5,1974,Europe,audi +volkswagen dasher,26,4,79,67,1963,15.5,1974,Europe,volkswagen +opel manta,26,4,97,78,2300,14.5,1974,Europe,opel +toyota corona,31,4,76,52,1649,16.5,1974,Japan,toyota +datsun 710,32,4,83,61,2003,19,1974,Japan,datsun +dodge colt,28,4,90,75,2125,14.5,1974,USA,dodge +fiat 128,24,4,90,75,2108,15.5,1974,Europe,fiat +fiat 124 tc,26,4,116,75,2246,14,1974,Europe,fiat +honda civic,24,4,120,97,2489,15,1974,Japan,honda +subaru,26,4,108,93,2391,15.5,1974,Japan,subaru +fiat x1.9,31,4,79,67,2000,16,1974,Europe,fiat +plymouth valiant custom,19,6,225,95,3264,16,1975,USA,plymouth +chevrolet nova,18,6,250,105,3459,16,1975,USA,chevrolet +mercury monarch,15,6,250,72,3432,21,1975,USA,mercury +ford maverick,15,6,250,72,3158,19.5,1975,USA,ford +pontiac catalina,16,8,400,170,4668,11.5,1975,USA,pontiac +chevrolet bel air,15,8,350,145,4440,14,1975,USA,chevrolet +plymouth grand fury,16,8,318,150,4498,14.5,1975,USA,plymouth +ford ltd,14,8,351,148,4657,13.5,1975,USA,ford +buick century,17,6,231,110,3907,21,1975,USA,buick +chevroelt chevelle malibu,16,6,250,105,3897,18.5,1975,USA,chevrolet +amc matador,15,6,258,110,3730,19,1975,USA,amc +plymouth fury,18,6,225,95,3785,19,1975,USA,plymouth +buick skyhawk,21,6,231,110,3039,15,1975,USA,buick +chevrolet monza 2+2,20,8,262,110,3221,13.5,1975,USA,chevrolet +ford mustang ii,13,8,302,129,3169,12,1975,USA,ford +toyota corolla,29,4,97,75,2171,16,1975,Japan,toyota +ford pinto,23,4,140,83,2639,17,1975,USA,ford +amc gremlin,20,6,232,100,2914,16,1975,USA,amc +pontiac astro,23,4,140,78,2592,18.5,1975,USA,pontiac +toyota corona,24,4,134,96,2702,13.5,1975,Japan,toyota +volkswagen dasher,25,4,90,71,2223,16.5,1975,Europe,volkswagen +datsun 710,24,4,119,97,2545,17,1975,Japan,datsun +ford pinto,18,6,171,97,2984,14.5,1975,USA,ford +volkswagen rabbit,29,4,90,70,1937,14,1975,Europe,volkswagen +amc pacer,19,6,232,90,3211,17,1975,USA,amc +audi 100ls,23,4,115,95,2694,15,1975,Europe,audi +peugeot 504,23,4,120,88,2957,17,1975,Europe,peugeot +volvo 244dl,22,4,121,98,2945,14.5,1975,Europe,volvo +saab 99le,25,4,121,115,2671,13.5,1975,Europe,saab +honda civic cvcc,33,4,91,53,1795,17.5,1975,Japan,honda +fiat 131,28,4,107,86,2464,15.5,1976,Europe,fiat +opel 1900,25,4,116,81,2220,16.9,1976,Europe,opel +capri ii,25,4,140,92,2572,14.9,1976,USA,ford +dodge colt,26,4,98,79,2255,17.7,1976,USA,dodge +renault 12tl,27,4,101,83,2202,15.3,1976,Europe,renault +chevrolet chevelle malibu classic,17.5,8,305,140,4215,13,1976,USA,chevrolet +dodge coronet brougham,16,8,318,150,4190,13,1976,USA,dodge +amc matador,15.5,8,304,120,3962,13.9,1976,USA,amc +ford gran torino,14.5,8,351,152,4215,12.8,1976,USA,ford +plymouth valiant,22,6,225,100,3233,15.4,1976,USA,plymouth +chevrolet nova,22,6,250,105,3353,14.5,1976,USA,chevrolet +ford maverick,24,6,200,81,3012,17.6,1976,USA,ford +amc hornet,22.5,6,232,90,3085,17.6,1976,USA,amc +chevrolet chevette,29,4,85,52,2035,22.2,1976,USA,chevrolet +chevrolet woody,24.5,4,98,60,2164,22.1,1976,USA,chevrolet +vw rabbit,29,4,90,70,1937,14.2,1976,Europe,volkswagen +honda civic,33,4,91,53,1795,17.4,1976,Japan,honda +dodge aspen se,20,6,225,100,3651,17.7,1976,USA,dodge +ford granada ghia,18,6,250,78,3574,21,1976,USA,ford +pontiac ventura sj,18.5,6,250,110,3645,16.2,1976,USA,pontiac +amc pacer d/l,17.5,6,258,95,3193,17.8,1976,USA,amc +volkswagen rabbit,29.5,4,97,71,1825,12.2,1976,Europe,volkswagen +datsun b-210,32,4,85,70,1990,17,1976,Japan,datsun +toyota corolla,28,4,97,75,2155,16.4,1976,Japan,toyota +ford pinto,26.5,4,140,72,2565,13.6,1976,USA,ford +volvo 245,20,4,130,102,3150,15.7,1976,Europe,volvo +plymouth volare premier v8,13,8,318,150,3940,13.2,1976,USA,plymouth +peugeot 504,19,4,120,88,3270,21.9,1976,Europe,peugeot +toyota mark ii,19,6,156,108,2930,15.5,1976,Japan,toyota +mercedes-benz 280s,16.5,6,168,120,3820,16.7,1976,Europe,mercedes +cadillac seville,16.5,8,350,180,4380,12.1,1976,USA,cadillac +chevy c10,13,8,350,145,4055,12,1976,USA,chevrolet +ford f108,13,8,302,130,3870,15,1976,USA,ford +dodge d100,13,8,318,150,3755,14,1976,USA,dodge +honda Accelerationord cvcc,31.5,4,98,68,2045,18.5,1977,Japan,honda +buick opel isuzu deluxe,30,4,111,80,2155,14.8,1977,USA,buick +renault 5 gtl,36,4,79,58,1825,18.6,1977,Europe,renault +plymouth arrow gs,25.5,4,122,96,2300,15.5,1977,USA,plymouth +datsun f-10 hatchback,33.5,4,85,70,1945,16.8,1977,Japan,datsun +chevrolet caprice classic,17.5,8,305,145,3880,12.5,1977,USA,chevrolet +oldsmobile cutlass supreme,17,8,260,110,4060,19,1977,USA,oldsmobile +dodge monaco brougham,15.5,8,318,145,4140,13.7,1977,USA,dodge +mercury cougar brougham,15,8,302,130,4295,14.9,1977,USA,mercury +chevrolet concours,17.5,6,250,110,3520,16.4,1977,USA,chevrolet +buick skylark,20.5,6,231,105,3425,16.9,1977,USA,buick +plymouth volare custom,19,6,225,100,3630,17.7,1977,USA,plymouth +ford granada,18.5,6,250,98,3525,19,1977,USA,ford +pontiac grand prix lj,16,8,400,180,4220,11.1,1977,USA,pontiac +chevrolet monte carlo landau,15.5,8,350,170,4165,11.4,1977,USA,chevrolet +chrysler cordoba,15.5,8,400,190,4325,12.2,1977,USA,chrysler +ford thunderbird,16,8,351,149,4335,14.5,1977,USA,ford +volkswagen rabbit custom,29,4,97,78,1940,14.5,1977,Europe,volkswagen +pontiac sunbird coupe,24.5,4,151,88,2740,16,1977,USA,pontiac +toyota corolla liftback,26,4,97,75,2265,18.2,1977,Japan,toyota +ford mustang ii 2+2,25.5,4,140,89,2755,15.8,1977,USA,ford +chevrolet chevette,30.5,4,98,63,2051,17,1977,USA,chevrolet +dodge colt m/m,33.5,4,98,83,2075,15.9,1977,USA,dodge +subaru dl,30,4,97,67,1985,16.4,1977,Japan,subaru +volkswagen dasher,30.5,4,97,78,2190,14.1,1977,Europe,volkswagen +datsun 810,22,6,146,97,2815,14.5,1977,Japan,datsun +bmw 320i,21.5,4,121,110,2600,12.8,1977,Europe,bmw +mazda rx-4,21.5,3,80,110,2720,13.5,1977,Japan,mazda +volkswagen rabbit custom diesel,43.1,4,90,48,1985,21.5,1978,Europe,volkswagen +ford fiesta,36.1,4,98,66,1800,14.4,1978,USA,ford +mazda glc deluxe,32.8,4,78,52,1985,19.4,1978,Japan,mazda +datsun b210 gx,39.4,4,85,70,2070,18.6,1978,Japan,datsun +honda civic cvcc,36.1,4,91,60,1800,16.4,1978,Japan,honda +oldsmobile cutlass salon brougham,19.9,8,260,110,3365,15.5,1978,USA,oldsmobile +dodge diplomat,19.4,8,318,140,3735,13.2,1978,USA,dodge +mercury monarch ghia,20.2,8,302,139,3570,12.8,1978,USA,mercury +pontiac phoenix lj,19.2,6,231,105,3535,19.2,1978,USA,pontiac +chevrolet malibu,20.5,6,200,95,3155,18.2,1978,USA,chevrolet +ford fairmont (auto),20.2,6,200,85,2965,15.8,1978,USA,ford +ford fairmont (man),25.1,4,140,88,2720,15.4,1978,USA,ford +plymouth volare,20.5,6,225,100,3430,17.2,1978,USA,plymouth +amc concord,19.4,6,232,90,3210,17.2,1978,USA,amc +buick century special,20.6,6,231,105,3380,15.8,1978,USA,buick +mercury zephyr,20.8,6,200,85,3070,16.7,1978,USA,mercury +dodge aspen,18.6,6,225,110,3620,18.7,1978,USA,dodge +amc concord d/l,18.1,6,258,120,3410,15.1,1978,USA,amc +chevrolet monte carlo landau,19.2,8,305,145,3425,13.2,1978,USA,chevrolet +buick regal sport coupe (turbo),17.7,6,231,165,3445,13.4,1978,USA,buick +ford futura,18.1,8,302,139,3205,11.2,1978,USA,ford +dodge magnum xe,17.5,8,318,140,4080,13.7,1978,USA,dodge +chevrolet chevette,30,4,98,68,2155,16.5,1978,USA,chevrolet +toyota corona,27.5,4,134,95,2560,14.2,1978,Japan,toyota +datsun 510,27.2,4,119,97,2300,14.7,1978,Japan,datsun +dodge omni,30.9,4,105,75,2230,14.5,1978,USA,dodge +toyota celica gt liftback,21.1,4,134,95,2515,14.8,1978,Japan,toyota +plymouth sapporo,23.2,4,156,105,2745,16.7,1978,USA,plymouth +oldsmobile starfire sx,23.8,4,151,85,2855,17.6,1978,USA,oldsmobile +datsun 200-sx,23.9,4,119,97,2405,14.9,1978,Japan,datsun +audi 5000,20.3,5,131,103,2830,15.9,1978,Europe,audi +volvo 264gl,17,6,163,125,3140,13.6,1978,Europe,volvo +saab 99gle,21.6,4,121,115,2795,15.7,1978,Europe,saab +peugeot 604sl,16.2,6,163,133,3410,15.8,1978,Europe,peugeot +volkswagen scirocco,31.5,4,89,71,1990,14.9,1978,Europe,volkswagen +honda Accelerationord lx,29.5,4,98,68,2135,16.6,1978,Japan,honda +pontiac lemans v6,21.5,6,231,115,3245,15.4,1979,USA,pontiac +mercury zephyr 6,19.8,6,200,85,2990,18.2,1979,USA,mercury +ford fairmont 4,22.3,4,140,88,2890,17.3,1979,USA,ford +amc concord dl 6,20.2,6,232,90,3265,18.2,1979,USA,amc +dodge aspen 6,20.6,6,225,110,3360,16.6,1979,USA,dodge +chevrolet caprice classic,17,8,305,130,3840,15.4,1979,USA,chevrolet +ford ltd landau,17.6,8,302,129,3725,13.4,1979,USA,ford +mercury grand marquis,16.5,8,351,138,3955,13.2,1979,USA,mercury +dodge st. regis,18.2,8,318,135,3830,15.2,1979,USA,dodge +buick estate wagon (sw),16.9,8,350,155,4360,14.9,1979,USA,buick +ford country squire (sw),15.5,8,351,142,4054,14.3,1979,USA,ford +chevrolet malibu classic (sw),19.2,8,267,125,3605,15,1979,USA,chevrolet +chrysler lebaron town @ country (sw),18.5,8,360,150,3940,13,1979,USA,chrysler +vw rabbit custom,31.9,4,89,71,1925,14,1979,Europe,volkswagen +maxda glc deluxe,34.1,4,86,65,1975,15.2,1979,Japan,mazda +dodge colt hatchback custom,35.7,4,98,80,1915,14.4,1979,USA,dodge +amc spirit dl,27.4,4,121,80,2670,15,1979,USA,amc +mercedes benz 300d,25.4,5,183,77,3530,20.1,1979,Europe,mercedes +cadillac eldorado,23,8,350,125,3900,17.4,1979,USA,cadillac +peugeot 504,27.2,4,141,71,3190,24.8,1979,Europe,peugeot +oldsmobile cutlass salon brougham,23.9,8,260,90,3420,22.2,1979,USA,oldsmobile +plymouth horizon,34.2,4,105,70,2200,13.2,1979,USA,plymouth +plymouth horizon tc3,34.5,4,105,70,2150,14.9,1979,USA,plymouth +datsun 210,31.8,4,85,65,2020,19.2,1979,Japan,datsun +fiat strada custom,37.3,4,91,69,2130,14.7,1979,Europe,fiat +buick skylark limited,28.4,4,151,90,2670,16,1979,USA,buick +chevrolet citation,28.8,6,173,115,2595,11.3,1979,USA,chevrolet +oldsmobile omega brougham,26.8,6,173,115,2700,12.9,1979,USA,oldsmobile +pontiac phoenix,33.5,4,151,90,2556,13.2,1979,USA,pontiac +vw rabbit,41.5,4,98,76,2144,14.7,1980,Europe,volkswagen +toyota corolla tercel,38.1,4,89,60,1968,18.8,1980,Japan,toyota +chevrolet chevette,32.1,4,98,70,2120,15.5,1980,USA,chevrolet +datsun 310,37.2,4,86,65,2019,16.4,1980,Japan,datsun +chevrolet citation,28,4,151,90,2678,16.5,1980,USA,chevrolet +ford fairmont,26.4,4,140,88,2870,18.1,1980,USA,ford +amc concord,24.3,4,151,90,3003,20.1,1980,USA,amc +dodge aspen,19.1,6,225,90,3381,18.7,1980,USA,dodge +audi 4000,34.3,4,97,78,2188,15.8,1980,Europe,audi +toyota corona liftback,29.8,4,134,90,2711,15.5,1980,Japan,toyota +mazda 626,31.3,4,120,75,2542,17.5,1980,Japan,mazda +datsun 510 hatchback,37,4,119,92,2434,15,1980,Japan,datsun +toyota corolla,32.2,4,108,75,2265,15.2,1980,Japan,toyota +mazda glc,46.6,4,86,65,2110,17.9,1980,Japan,mazda +dodge colt,27.9,4,156,105,2800,14.4,1980,USA,dodge +datsun 210,40.8,4,85,65,2110,19.2,1980,Japan,datsun +vw rabbit c (diesel),44.3,4,90,48,2085,21.7,1980,Europe,volkswagen +vw dasher (diesel),43.4,4,90,48,2335,23.7,1980,Europe,volkswagen +audi 5000s (diesel),36.4,5,121,67,2950,19.9,1980,Europe,audi +mercedes-benz 240d,30,4,146,67,3250,21.8,1980,Europe,mercedes +honda civic 1500 gl,44.6,4,91,67,1850,13.8,1980,Japan,honda +subaru dl,33.8,4,97,67,2145,18,1980,Japan,subaru +vokswagen rabbit,29.8,4,89,62,1845,15.3,1980,Europe,volkswagen +datsun 280-zx,32.7,6,168,132,2910,11.4,1980,Japan,datsun +mazda rx-7 gs,23.7,3,70,100,2420,12.5,1980,Japan,mazda +triumph tr7 coupe,35,4,122,88,2500,15.1,1980,Europe,triumph +honda Accelerationord,32.4,4,107,72,2290,17,1980,Japan,honda +plymouth reliant,27.2,4,135,84,2490,15.7,1982,USA,plymouth +buick skylark,26.6,4,151,84,2635,16.4,1982,USA,buick +dodge aries wagon (sw),25.8,4,156,92,2620,14.4,1982,USA,dodge +chevrolet citation,23.5,6,173,110,2725,12.6,1982,USA,chevrolet +plymouth reliant,30,4,135,84,2385,12.9,1982,USA,plymouth +toyota starlet,39.1,4,79,58,1755,16.9,1982,Japan,toyota +plymouth champ,39,4,86,64,1875,16.4,1982,USA,plymouth +honda civic 1300,35.1,4,81,60,1760,16.1,1982,Japan,honda +subaru,32.3,4,97,67,2065,17.8,1982,Japan,subaru +datsun 210,37,4,85,65,1975,19.4,1982,Japan,datsun +toyota tercel,37.7,4,89,62,2050,17.3,1982,Japan,toyota +mazda glc 4,34.1,4,91,68,1985,16,1982,Japan,mazda +plymouth horizon 4,34.7,4,105,63,2215,14.9,1982,USA,plymouth +ford escort 4w,34.4,4,98,65,2045,16.2,1982,USA,ford +ford escort 2h,29.9,4,98,65,2380,20.7,1982,USA,ford +volkswagen jetta,33,4,105,74,2190,14.2,1982,Europe,volkswagen +honda prelude,33.7,4,107,75,2210,14.4,1982,Japan,honda +toyota corolla,32.4,4,108,75,2350,16.8,1982,Japan,toyota +datsun 200sx,32.9,4,119,100,2615,14.8,1982,Japan,datsun +mazda 626,31.6,4,120,74,2635,18.3,1982,Japan,mazda +peugeot 505s turbo diesel,28.1,4,141,80,3230,20.4,1982,Europe,peugeot +volvo diesel,30.7,6,145,76,3160,19.6,1982,Europe,volvo +toyota cressida,25.4,6,168,116,2900,12.6,1982,Japan,toyota +datsun 810 maxima,24.2,6,146,120,2930,13.8,1982,Japan,datsun +buick century,22.4,6,231,110,3415,15.8,1982,USA,buick +oldsmobile cutlass ls,26.6,8,350,105,3725,19,1982,USA,oldsmobile +ford granada gl,20.2,6,200,88,3060,17.1,1982,USA,ford +chrysler lebaron salon,17.6,6,225,85,3465,16.6,1982,USA,chrysler +chevrolet cavalier,28,4,112,88,2605,19.6,1982,USA,chevrolet +chevrolet cavalier wagon,27,4,112,88,2640,18.6,1982,USA,chevrolet +chevrolet cavalier 2-door,34,4,112,88,2395,18,1982,USA,chevrolet +pontiac j2000 se hatchback,31,4,112,85,2575,16.2,1982,USA,pontiac +dodge aries se,29,4,135,84,2525,16,1982,USA,dodge +pontiac phoenix,27,4,151,90,2735,18,1982,USA,pontiac +ford fairmont futura,24,4,140,92,2865,16.4,1982,USA,ford +volkswagen rabbit l,36,4,105,74,1980,15.3,1982,Europe,volkswagen +mazda glc custom l,37,4,91,68,2025,18.2,1982,Japan,mazda +mazda glc custom,31,4,91,68,1970,17.6,1982,Japan,mazda +plymouth horizon miser,38,4,105,63,2125,14.7,1982,USA,plymouth +mercury lynx l,36,4,98,70,2125,17.3,1982,USA,mercury +nissan stanza xe,36,4,120,88,2160,14.5,1982,Japan,nissan +honda Accelerationord,36,4,107,75,2205,14.5,1982,Japan,honda +toyota corolla,34,4,108,70,2245,16.9,1982,Japan,toyota +honda civic,38,4,91,67,1965,15,1982,Japan,honda +honda civic (auto),32,4,91,67,1965,15.7,1982,Japan,honda +datsun 310 gx,38,4,91,67,1995,16.2,1982,Japan,datsun +buick century limited,25,6,181,110,2945,16.4,1982,USA,buick +oldsmobile cutlass ciera (diesel),38,6,262,85,3015,17,1982,USA,oldsmobile +chrysler lebaron medallion,26,4,156,92,2585,14.5,1982,USA,chrysler +ford granada l,22,6,232,112,2835,14.7,1982,USA,ford +toyota celica gt,32,4,144,96,2665,13.9,1982,Japan,toyota +dodge charger 2.2,36,4,135,84,2370,13,1982,USA,dodge +chevrolet camaro,27,4,151,90,2950,17.3,1982,USA,chevrolet +ford mustang gl,27,4,140,86,2790,15.6,1982,USA,ford +vw pickup,44,4,97,52,2130,24.6,1982,Europe,volkswagen +dodge rampage,32,4,135,84,2295,11.6,1982,USA,dodge +ford ranger,28,4,120,79,2625,18.6,1982,USA,ford +chevy s-10,31,4,119,82,2720,19.4,1982,USA,chevrolet \ No newline at end of file diff --git a/tests/test_compiler.py b/tests/test_compiler.py index be3c32e8..e2079ab6 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -155,8 +155,7 @@ def test_sort_bar(): def test_specified_vis_collection(): - url = "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" - df = pd.read_csv(url) + df = pd.read_csv("lux/data/car.csv") df["Year"] = pd.to_datetime( df["Year"], format="%Y" ) # change pandas dtype for the column "Year" to datetype diff --git a/tests/test_dates.py b/tests/test_dates.py index 140a2450..462dac47 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -93,7 +93,7 @@ def test_period_to_altair(): exported_code = df.recommendation["Filter"][2].to_Altair() - assert "Year = 1971" in exported_code + assert "Year = 1972" in exported_code def test_refresh_inplace(): diff --git a/tests/test_interestingness.py b/tests/test_interestingness.py index 7fa7fcd0..a42766ce 100644 --- a/tests/test_interestingness.py +++ b/tests/test_interestingness.py @@ -51,9 +51,9 @@ def test_interestingness_1_0_0(): if int(vis._inferred_intent[2].value) == 8: rank1 = f if int(vis._inferred_intent[2].value) == 6: - rank2 = f - if "1972" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): - rank3 = f + rank3 = f + if "ford" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): + rank2 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 @@ -112,7 +112,7 @@ def test_interestingness_0_1_0(): rank1 = f if str(df.recommendation["Filter"][f]._inferred_intent[2].value) == "Europe": rank2 = f - if "1971" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): + if "1970" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 @@ -151,11 +151,11 @@ def test_interestingness_1_1_0(): if len(vis.get_attr_by_attr_name("Cylinders")) > 0: if int(vis._inferred_intent[2].value) == 6: rank1 = f - if int(vis._inferred_intent[2].value) == 5: - rank3 = f + if int(vis._inferred_intent[2].value) == 8: + rank2 = f if len(vis.get_attr_by_attr_name("Origin")) > 0: if str(vis._inferred_intent[2].value) == "Europe": - rank2 = f + rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 # check that top recommended generalize graph score is not none @@ -261,9 +261,9 @@ def test_interestingness_0_2_0(): for f in range(0, len(df.recommendation["Filter"])): if "1973" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank1 = f - if "1976" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): + if "ford" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank2 = f - if str(df.recommendation["Filter"][f]._inferred_intent[2].value) == "Europe": + if str(df.recommendation["Filter"][f]._inferred_intent[2].value) == "USA": rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 diff --git a/tests/test_maintainence.py b/tests/test_maintainence.py index 20ee227e..c336236c 100644 --- a/tests/test_maintainence.py +++ b/tests/test_maintainence.py @@ -77,12 +77,12 @@ def test_recs_inplace_operation(): df = pd.read_csv("lux/data/car.csv") df._repr_html_() assert df._recs_fresh == True, "Failed to maintain recommendation after display df" - assert len(df.recommendation["Occurrence"]) == 3 + assert len(df.recommendation["Occurrence"]) == 4 df.drop(columns=["Name"], inplace=True) assert "Name" not in df.columns, "Failed to perform `drop` operation in-place" assert ( df._recs_fresh == False ), "Failed to maintain recommendation after in-place Pandas operation" df._repr_html_() - assert len(df.recommendation["Occurrence"]) == 2 + assert len(df.recommendation["Occurrence"]) == 3 assert df._recs_fresh == True, "Failed to maintain recommendation after display df" diff --git a/tests/test_pandas_coverage.py b/tests/test_pandas_coverage.py index d88badf5..ad59abe1 100644 --- a/tests/test_pandas_coverage.py +++ b/tests/test_pandas_coverage.py @@ -111,6 +111,7 @@ def test_rename3(): "col7", "col8", "col9", + "col10", ] df._repr_html_() assert list(df.recommendation.keys()) == [ @@ -119,7 +120,7 @@ def test_rename3(): "Occurrence", "Temporal", ] - assert len(df.cardinality) == 9 + assert len(df.cardinality) == 10 assert "col2" in list(df.cardinality.keys()) @@ -194,7 +195,7 @@ def test_query(): "Occurrence", "Temporal", ] - assert len(new_df.cardinality) == 9 + assert len(new_df.cardinality) == 10 def test_pop(): @@ -208,7 +209,7 @@ def test_pop(): "Occurrence", "Temporal", ] - assert len(df.cardinality) == 8 + assert len(df.cardinality) == 9 def test_transform(): @@ -217,7 +218,7 @@ def test_transform(): new_df = df.iloc[:, 1:].groupby("Origin").transform(sum) new_df._repr_html_() assert list(new_df.recommendation.keys()) == ["Correlation", "Occurrence"] - assert len(new_df.cardinality) == 6 + assert len(new_df.cardinality) == 7 def test_get_group(): @@ -232,7 +233,7 @@ def test_get_group(): "Occurrence", "Temporal", ] - assert len(new_df.cardinality) == 9 + assert len(new_df.cardinality) == 10 def test_applymap(): @@ -247,13 +248,11 @@ def test_applymap(): "Occurrence", "Temporal", ] - assert len(df.cardinality) == 9 + assert len(df.cardinality) == 10 def test_strcat(): - df = pd.read_csv( - "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" - ) + df = pd.read_csv("lux/data/car.csv") df["Year"] = pd.to_datetime(df["Year"], format="%Y") df["combined"] = df["Origin"].str.cat(df["Brand"], sep=", ") df._repr_html_() @@ -267,9 +266,7 @@ def test_strcat(): def test_named_agg(): - df = pd.read_csv( - "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" - ) + df = pd.read_csv("lux/data/car.csv") df["Year"] = pd.to_datetime(df["Year"], format="%Y") new_df = df.groupby("Brand").agg( avg_weight=("Weight", "mean"), @@ -292,7 +289,7 @@ def test_change_dtype(): "Occurrence", "Temporal", ] - assert len(df.data_type_lookup) == 9 + assert len(df.data_type_lookup) == 10 def test_get_dummies(): @@ -306,7 +303,7 @@ def test_get_dummies(): "Occurrence", "Temporal", ] - assert len(new_df.data_type_lookup) == 310 + assert len(new_df.data_type_lookup) == 339 def test_drop(): @@ -321,7 +318,7 @@ def test_drop(): "Occurrence", "Temporal", ] - assert len(new_df2.cardinality) == 6 + assert len(new_df2.cardinality) == 7 def test_merge(): @@ -336,7 +333,7 @@ def test_merge(): "Occurrence", "Temporal", ] # TODO once bug is fixed - assert len(new_df2.cardinality) == 10 + assert len(new_df2.cardinality) == 11 def test_prefix(): @@ -350,14 +347,12 @@ def test_prefix(): "Occurrence", "Temporal", ] - assert len(new_df.cardinality) == 9 + assert len(new_df.cardinality) == 10 assert new_df.cardinality["1_Name"] == 300 def test_loc(): - df = pd.read_csv( - "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" - ) + df = pd.read_csv("lux/data/car.csv") df["Year"] = pd.to_datetime(df["Year"], format="%Y") new_df = df.loc[:, "Displacement":"Origin"] new_df._repr_html_() @@ -388,9 +383,7 @@ def test_loc(): def test_iloc(): - df = pd.read_csv( - "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" - ) + df = pd.read_csv("lux/data/car.csv") df["Year"] = pd.to_datetime(df["Year"], format="%Y") new_df = df.iloc[:, 3:9] new_df._repr_html_() @@ -563,8 +556,7 @@ def test_value_counts(): def test_str_replace(): - url = "https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true" - df = pd.read_csv(url) + df = pd.read_csv("lux/data/car.csv") df._repr_html_() # compute metadata assert df.cardinality is not None series = df["Brand"].str.replace("chevrolet", "chevy") From cb4c90fafd1d8169e02fba8b854999d4f366a639 Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Thu, 5 Nov 2020 04:56:04 +0800 Subject: [PATCH 038/114] fix broken link in docs --- doc/source/getting_started/overview.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/getting_started/overview.rst b/doc/source/getting_started/overview.rst index e40ef5e9..c873c1f8 100644 --- a/doc/source/getting_started/overview.rst +++ b/doc/source/getting_started/overview.rst @@ -95,7 +95,7 @@ When you print out the dataframe again, you should see three different tabs of v df -.. image:: ../../../../lux-resources/doc_img/overview-3.gif +.. image:: https://github.com/lux-org/lux-resources/blob/master/doc_img/overview-3.gif?raw=true :width: 700 :align: center :alt: scroll through Enhance, click on Filter tab @@ -129,7 +129,7 @@ Given the updated intent, additional actions (Enhance and Filter) are generated. - {MedianEarnings, **AverageCost**} - {MedianEarnings, **AverageFacultySalary**}. -.. image:: ../img/overview-4.png +.. image:: https://github.com/lux-org/lux-resources/blob/master/doc_img/overview-4.png :width: 700 :align: center :alt: screenshot of Enhance @@ -140,7 +140,7 @@ Given the updated intent, additional actions (Enhance and Filter) are generated. - {MedianEarnings, **Region=Southeast**} - {MedianEarnings, **Region=Great Lakes**}. -.. image:: ../img/overview-5.png +.. image:: https://github.com/lux-org/lux-resources/blob/master/doc_img/overview-5.png :width: 700 :align: center :alt: screenshot of Filter From 8029ac1346aa82eed3428dfbb0cafd57dba790ed Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Fri, 6 Nov 2020 14:25:18 -0800 Subject: [PATCH 039/114] Updated Tests, Added benchmarking for SQL Executor Updated Compiler and Interestingness tests to work for SQL executor. Updated SQL Executor to have some benchmarking code for tracking query performance. --- lux/core/frame.py | 4 +- lux/executor/SQLExecutor.py | 68 ++++++- sql_benchmarking.csv | 342 ++++++++++++++++++++++++++++++++++ tests/test_compiler.py | 178 ++++++++++++++++++ tests/test_interestingness.py | 242 +++++++++++++++--------- 5 files changed, 740 insertions(+), 94 deletions(-) create mode 100644 sql_benchmarking.csv diff --git a/lux/core/frame.py b/lux/core/frame.py index 14ce35c5..f30c6e06 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -298,7 +298,7 @@ def set_SQL_connection(self, connection, t_name): self.SQLconnection = connection self.table_name = t_name self.set_executor_type("SQL") - self.executor.compute_dataset_metadata(self) + self.maintain_metadata() def _append_rec(self,rec_infolist,recommendations:Dict): if (recommendations["collection"] is not None and len(recommendations["collection"])>0): @@ -461,7 +461,7 @@ def _repr_html_(self): from IPython.display import display from IPython.display import clear_output import ipywidgets as widgets - + try: if (self._pandas_only): display(self.display_pandas()) diff --git a/lux/executor/SQLExecutor.py b/lux/executor/SQLExecutor.py index eee5d3cd..e034cd6d 100644 --- a/lux/executor/SQLExecutor.py +++ b/lux/executor/SQLExecutor.py @@ -8,6 +8,9 @@ import math +#for benchmarking +import time + class SQLExecutor(Executor): """ Given a Vis objects with complete specifications, fetch and process data using SQL operations. @@ -29,16 +32,16 @@ def execute(view_collection:VisList, ldf: LuxDataFrame): 2) Retreive relevant attribute 3) return a DataFrame with relevant results ''' + for view in view_collection: # Select relevant data based on attribute information attributes = set([]) for clause in view._inferred_intent: if (clause.attribute): - if (clause.attribute=="Record"): + if (clause.attribute!="Record"): attributes.add(clause.attribute) - #else: - attributes.add(clause.attribute) if view.mark not in ["bar", "line", "histogram"]: + start = time.time() where_clause, filterVars = SQLExecutor.execute_filter(view) length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) @@ -53,10 +56,30 @@ def execute(view_collection:VisList, ldf: LuxDataFrame): data = pandas.read_sql(query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(data) view._vis_data.length = list(length_query['length'])[0] + end = time.time() + #append benchmark data to file + benchmark_data = {'executor_name':['SQL'], 'query_action':['scatter'], 'time':[end-start], 'length':[ldf.length]} + benchmark_df = pandas.DataFrame(data = benchmark_data) + benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + #print("time to collect scatterplot data", end-start) if (view.mark =="bar" or view.mark =="line"): + start = time.time() SQLExecutor.execute_aggregate(view, ldf) + end = time.time() + #append benchmark data to file + benchmark_data = {'executor_name':['SQL'], 'query_action':['bar/line'], 'time':[end-start], 'length':[ldf.length]} + benchmark_df = pandas.DataFrame(data = benchmark_data) + benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + #print("time to collect bar/line data", end-start) elif (view.mark =="histogram"): + start = time.time() SQLExecutor.execute_binning(view, ldf) + end = time.time() + #append benchmark data to file + benchmark_data = {'executor_name':['SQL'], 'query_action':['binning'], 'time':[end-start], 'length':[ldf.length]} + benchmark_df = pandas.DataFrame(data = benchmark_data) + benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + #print("time to collect histogram data", end-start) @staticmethod def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): @@ -301,6 +324,7 @@ def compute_dataset_metadata(self, ldf:LuxDataFrame): self.compute_data_model(ldf) def get_SQL_attributes(self, ldf:LuxDataFrame): + start = time.time() if "." in ldf.table_name: table_name = ldf.table_name[self.table_name.index(".")+1:] else: @@ -309,32 +333,63 @@ def get_SQL_attributes(self, ldf:LuxDataFrame): attributes = list(pandas.read_sql(attr_query, ldf.SQLconnection)['column_name']) for attr in attributes: ldf[attr] = None + end = time.time() + #append benchmark data to file + benchmark_data = {'executor_name':['SQL'], 'query_action':['get_attributes'], 'time':[end-start], 'length':[ldf.length]} + benchmark_df = pandas.DataFrame(data = benchmark_data) + benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) def compute_stats(self, ldf:LuxDataFrame): # precompute statistics ldf.unique_values = {} ldf._min_max = {} + start = time.time() length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {}".format(ldf.table_name), ldf.SQLconnection) + end = time.time() + #append benchmark data to file + benchmark_data = {'executor_name':['SQL'], 'query_action':['data_length'], 'time':[end-start], 'length':[ldf.length]} + benchmark_df = pandas.DataFrame(data = benchmark_data) ldf.length = list(length_query['length'])[0] + benchmark_df.to_pandas().to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) self.get_unique_values(ldf) #ldf.get_cardinality() for attribute in ldf.columns: if ldf.data_type_lookup[attribute] == 'quantitative': + start = time.time() min_max_query = pandas.read_sql("SELECT MIN({}) as min, MAX({}) as max FROM {}".format(attribute, attribute, ldf.table_name), ldf.SQLconnection) + end=time.time() ldf._min_max[attribute] = (list(min_max_query["min"])[0], list(min_max_query['max'])[0]) + #append benchmark data to file + benchmark_data = {'executor_name':['SQL'], 'query_action':['min_max'], 'time':[end-start], 'length':[ldf.length]} + benchmark_df = pandas.DataFrame(data = benchmark_data) + benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) def get_cardinality(self, ldf:LuxDataFrame): cardinality = {} for attr in list(ldf.columns): + start = time.time() card_query = pandas.read_sql("SELECT Count(Distinct({})) FROM {}".format(attr, ldf.table_name), ldf.SQLconnection) + end = time.time() + #append benchmark data to file + benchmark_data = {'executor_name':['SQL'], 'query_action':['cardinality'], 'time':[end-start], 'length':[ldf.length]} + benchmark_df = pandas.DataFrame(data = benchmark_data) + benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + cardinality[attr] = list(card_query["count"])[0] ldf.cardinality = cardinality def get_unique_values(self, ldf:LuxDataFrame): unique_vals = {} for attr in list(ldf.columns): + start = time.time() unique_query = pandas.read_sql("SELECT Distinct({}) FROM {}".format(attr, ldf.table_name), ldf.SQLconnection) + end = time.time() + #append benchmark data to file + benchmark_data = {'executor_name':['SQL'], 'query_action':['unique_values'], 'time':[end-start], 'length':[ldf.length]} + benchmark_df = pandas.DataFrame(data = benchmark_data) + benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + unique_vals[attr] = list(unique_query[attr]) ldf.unique_values = unique_vals @@ -348,8 +403,15 @@ def compute_data_type(self, ldf:LuxDataFrame): table_name = ldf.table_name #get the data types of the attributes in the SQL table for attr in list(ldf.columns): + start = time.time() datatype_query = "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{}' AND COLUMN_NAME = '{}'".format(table_name, attr) datatype = list(pandas.read_sql(datatype_query, ldf.SQLconnection)['data_type'])[0] + end = time.time() + #append benchmark data to file + benchmark_data = {'executor_name':['SQL'], 'query_action':['data_type'], 'time':[end-start], 'length':[ldf.length]} + benchmark_df = pandas.DataFrame(data = benchmark_data) + benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + sql_dtypes[attr] = datatype data_type = {"quantitative":[], "ordinal":[], "nominal":[], "temporal":[], "id":[]} diff --git a/sql_benchmarking.csv b/sql_benchmarking.csv new file mode 100644 index 00000000..725d5254 --- /dev/null +++ b/sql_benchmarking.csv @@ -0,0 +1,342 @@ +executor_name,query_action,time,length +SQL,data_length,0.8699960708618164, +SQL,get_attributes,0.022000789642333984, +SQL,cardinality,0.23900198936462402, +SQL,cardinality,0.8590314388275146, +SQL,cardinality,0.24300098419189453, +SQL,cardinality,0.24300074577331543, +SQL,cardinality,0.8979983329772949, +SQL,cardinality,1.4349722862243652, +SQL,cardinality,1.4909999370574951, +SQL,cardinality,0.2719850540161133, +SQL,cardinality,0.2609994411468506, +SQL,cardinality,0.2989978790283203, +SQL,cardinality,0.3519899845123291, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003002166748046875, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0039598941802978516, +SQL,data_type,0.002994060516357422, +SQL,data_type,0.0040171146392822266, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003002166748046875, +SQL,data_type,0.0040280818939208984, +SQL,data_type,0.003973960876464844, +SQL,data_length,1.0710387229919434, +SQL,unique_values,0.22799944877624512,855632 +SQL,unique_values,0.2039933204650879,855632 +SQL,unique_values,0.16900134086608887,855632 +SQL,unique_values,0.19300055503845215,855632 +SQL,unique_values,0.2109992504119873,855632 +SQL,unique_values,0.24501585960388184,855632 +SQL,unique_values,0.24298477172851562,855632 +SQL,unique_values,0.2200484275817871,855632 +SQL,unique_values,0.21899843215942383,855632 +SQL,unique_values,0.2610325813293457,855632 +SQL,unique_values,0.2160017490386963,855632 +SQL,min_max,0.7019977569580078,855632 +SQL,min_max,0.37699198722839355,855632 +SQL,min_max,0.7890007495880127,855632 +SQL,min_max,1.1259708404541016,855632 +SQL,scatter,2.002000331878662,855632 +SQL,scatter,2.028999090194702,855632 +SQL,scatter,1.9170153141021729,855632 +SQL,scatter,2.456651210784912,855632 +SQL,scatter,1.2779710292816162,855632 +SQL,scatter,1.7619969844818115,855632 +SQL,scatter,2.119966506958008,855632 +SQL,scatter,1.835998296737671,855632 +SQL,scatter,1.9359958171844482,855632 +SQL,scatter,2.2759954929351807,855632 +SQL,scatter,1.4490406513214111,855632 +SQL,scatter,2.474003553390503,855632 +SQL,binning,1.6050374507904053,855632 +SQL,binning,1.5199933052062988,855632 +SQL,binning,1.8660304546356201,855632 +SQL,binning,2.2249794006347656,855632 +SQL,bar/line,2.020970106124878,855632 +SQL,bar/line,1.3449654579162598,855632 +SQL,bar/line,1.7890357971191406,855632 +SQL,bar/line,1.884040117263794,855632 +SQL,bar/line,1.0639941692352295,855632 +SQL,bar/line,1.1079692840576172,855632 +SQL,bar/line,1.3920071125030518,855632 +SQL,data_length,0.2279646396636963, +SQL,get_attributes,0.07999944686889648, +SQL,cardinality,0.039000511169433594, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0020084381103515625, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010161399841308594, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0020275115966796875, +SQL,cardinality,0.0009696483612060547, +SQL,cardinality,0.001008749008178711, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003956794738769531, +SQL,data_type,0.005998849868774414, +SQL,data_type,0.004030466079711914, +SQL,data_type,0.003972053527832031, +SQL,data_type,0.00302886962890625, +SQL,data_type,0.003992557525634766, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.0, +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.007996797561645508,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.001035928726196289,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0010006427764892578,392 +SQL,bar/line,0.06202960014343262,392 +SQL,scatter,0.016005992889404297,392 +SQL,scatter,0.0060007572174072266,392 +SQL,bar/line,0.025000572204589844,392 +SQL,bar/line,0.014998674392700195,392 +SQL,scatter,0.01799941062927246,392 +SQL,scatter,0.005998373031616211,392 +SQL,bar/line,0.0049974918365478516,392 +SQL,bar/line,0.009031057357788086,392 +SQL,bar/line,0.00500035285949707,392 +SQL,bar/line,0.009000062942504883,392 +SQL,bar/line,0.008969545364379883,392 +SQL,bar/line,0.00500035285949707,392 +SQL,bar/line,0.005001544952392578,392 +SQL,bar/line,0.00500035285949707,392 +SQL,bar/line,0.006000041961669922,392 +SQL,bar/line,0.004997968673706055,392 +SQL,bar/line,0.004985809326171875,392 +SQL,bar/line,0.007005214691162109,392 +SQL,bar/line,0.006029605865478516,392 +SQL,bar/line,0.00496363639831543,392 +SQL,bar/line,0.0050351619720458984,392 +SQL,bar/line,0.004996538162231445,392 +SQL,bar/line,0.00495147705078125,392 +SQL,bar/line,0.003999948501586914,392 +SQL,bar/line,0.00499725341796875,392 +SQL,bar/line,0.00599980354309082,392 +SQL,bar/line,0.003999948501586914,392 +SQL,bar/line,0.0049991607666015625,392 +SQL,bar/line,0.004999876022338867,392 +SQL,bar/line,0.004998207092285156,392 +SQL,bar/line,0.005998849868774414,392 +SQL,bar/line,0.005000114440917969,392 +SQL,bar/line,0.005982875823974609,392 +SQL,bar/line,0.004999637603759766,392 +SQL,bar/line,0.004996538162231445,392 +SQL,bar/line,0.0049974918365478516,392 +SQL,bar/line,0.005000591278076172,392 +SQL,bar/line,0.004999399185180664,392 +SQL,bar/line,0.004996776580810547,392 +SQL,bar/line,0.004000186920166016,392 +SQL,bar/line,0.0059969425201416016,392 +SQL,binning,0.003998279571533203,392 +SQL,bar/line,0.005000591278076172,392 +SQL,data_length,0.4390373229980469, +SQL,get_attributes,0.022954702377319336, +SQL,cardinality,0.23603153228759766, +SQL,cardinality,0.9599747657775879, +SQL,cardinality,0.30799126625061035, +SQL,cardinality,0.2690298557281494, +SQL,cardinality,1.008005142211914, +SQL,cardinality,1.5690290927886963, +SQL,cardinality,1.6099779605865479, +SQL,cardinality,0.23402714729309082, +SQL,cardinality,0.30198097229003906, +SQL,cardinality,0.3039853572845459, +SQL,cardinality,0.2910025119781494, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004006385803222656, +SQL,data_type,0.00400543212890625, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0039865970611572266, +SQL,data_type,0.0029973983764648438, +SQL,data_type,0.003973484039306641, +SQL,data_type,0.002974271774291992, +SQL,data_type,0.002972841262817383, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.1019754409790039, +SQL,unique_values,0.18699216842651367,855632 +SQL,unique_values,0.19903802871704102,855632 +SQL,unique_values,0.2119584083557129,855632 +SQL,unique_values,0.2320091724395752,855632 +SQL,unique_values,0.40199899673461914,855632 +SQL,unique_values,0.26400327682495117,855632 +SQL,unique_values,0.2580232620239258,855632 +SQL,unique_values,0.23099470138549805,855632 +SQL,unique_values,0.21199774742126465,855632 +SQL,unique_values,0.22099685668945312,855632 +SQL,unique_values,0.23600482940673828,855632 +SQL,min_max,0.13499760627746582,855632 +SQL,min_max,0.13000059127807617,855632 +SQL,min_max,0.1269996166229248,855632 +SQL,min_max,0.136993408203125,855632 +SQL,scatter,0.5110056400299072,855632 +SQL,bar/line,0.24703669548034668,855632 +SQL,bar/line,0.2299642562866211,855632 +SQL,bar/line,0.24700164794921875,855632 +SQL,bar/line,0.25499773025512695,855632 +SQL,bar/line,0.22700190544128418,855632 +SQL,bar/line,0.22902965545654297,855632 +SQL,bar/line,0.22299933433532715,855632 +SQL,scatter,0.4289736747741699,855632 +SQL,scatter,0.46297359466552734,855632 +SQL,binning,0.16697144508361816,855632 +SQL,binning,0.1860051155090332,855632 +SQL,binning,0.1529994010925293,855632 +SQL,binning,0.1509997844696045,855632 +SQL,binning,0.15399837493896484,855632 +SQL,binning,0.17000150680541992,855632 +SQL,binning,0.24300146102905273,855632 +SQL,binning,0.16000032424926758,855632 +SQL,binning,0.16599798202514648,855632 +SQL,binning,0.15699982643127441,855632 +SQL,binning,0.17099928855895996,855632 +SQL,binning,0.15602993965148926,855632 +SQL,binning,0.17097210884094238,855632 +SQL,binning,0.19099950790405273,855632 +SQL,binning,0.178999662399292,855632 +SQL,binning,0.1770000457763672,855632 +SQL,binning,0.1810007095336914,855632 +SQL,binning,0.1589975357055664,855632 +SQL,binning,0.14600157737731934,855632 +SQL,binning,0.2389979362487793,855632 +SQL,binning,0.1509692668914795,855632 +SQL,binning,0.24400067329406738,855632 +SQL,binning,0.19100356101989746,855632 +SQL,binning,0.18199706077575684,855632 +SQL,binning,0.19204258918762207,855632 +SQL,binning,0.1809558868408203,855632 +SQL,binning,0.1620008945465088,855632 +SQL,binning,0.17702770233154297,855632 +SQL,binning,0.17197012901306152,855632 +SQL,binning,0.17502617835998535,855632 +SQL,binning,0.1770002841949463,855632 +SQL,binning,0.15999364852905273,855632 +SQL,binning,0.1810016632080078,855632 +SQL,binning,0.17600631713867188,855632 +SQL,binning,0.1679975986480713,855632 +SQL,binning,0.18300127983093262,855632 +SQL,binning,0.16299724578857422,855632 +SQL,binning,0.23700404167175293,855632 +SQL,binning,0.17902159690856934,855632 +SQL,binning,0.16402435302734375,855632 +SQL,binning,0.1809694766998291,855632 +SQL,binning,0.18699932098388672,855632 +SQL,binning,0.17499852180480957,855632 +SQL,binning,0.22701239585876465,855632 +SQL,binning,0.2349870204925537,855632 +SQL,binning,0.20799946784973145,855632 +SQL,binning,0.21599698066711426,855632 +SQL,binning,0.20099854469299316,855632 +SQL,binning,0.19900250434875488,855632 +SQL,binning,0.22300028800964355,855632 +SQL,binning,0.19199609756469727,855632 +SQL,binning,0.19299960136413574,855632 +SQL,binning,0.18200325965881348,855632 +SQL,binning,0.17400002479553223,855632 +SQL,binning,0.17800235748291016,855632 +SQL,binning,0.17699813842773438,855632 +SQL,binning,0.19500064849853516,855632 +SQL,binning,0.17497730255126953,855632 +SQL,binning,0.1929621696472168,855632 +SQL,binning,0.17600107192993164,855632 +SQL,binning,0.19400453567504883,855632 +SQL,binning,0.18999528884887695,855632 +SQL,binning,0.17762160301208496,855632 +SQL,binning,0.1789999008178711,855632 +SQL,binning,0.1880016326904297,855632 +SQL,binning,0.17799901962280273,855632 +SQL,binning,0.17299842834472656,855632 +SQL,binning,0.1919994354248047,855632 +SQL,binning,0.18200135231018066,855632 +SQL,binning,0.17800188064575195,855632 +SQL,binning,0.21900367736816406,855632 +SQL,binning,0.17899870872497559,855632 +SQL,binning,0.26000332832336426,855632 +SQL,binning,0.2559993267059326,855632 +SQL,binning,0.24900007247924805,855632 +SQL,binning,0.26599836349487305,855632 +SQL,binning,0.234999418258667,855632 +SQL,binning,0.2360219955444336,855632 +SQL,binning,0.21602606773376465,855632 +SQL,binning,0.23002862930297852,855632 +SQL,binning,0.23699641227722168,855632 +SQL,binning,0.21099066734313965,855632 +SQL,binning,0.2220296859741211,855632 +SQL,binning,0.21299481391906738,855632 +SQL,binning,0.2259986400604248,855632 +SQL,binning,0.22899937629699707,855632 +SQL,binning,0.2869997024536133,855632 +SQL,binning,0.23699665069580078,855632 +SQL,binning,0.25300168991088867,855632 +SQL,binning,0.2189939022064209,855632 +SQL,binning,0.2500288486480713,855632 +SQL,binning,0.21497344970703125,855632 +SQL,binning,0.2259969711303711,855632 +SQL,binning,0.20899105072021484,855632 +SQL,binning,0.23602628707885742,855632 +SQL,binning,0.22900009155273438,855632 +SQL,binning,0.21903157234191895,855632 +SQL,binning,0.2899963855743408,855632 +SQL,binning,0.2609999179840088,855632 +SQL,binning,0.255995512008667,855632 +SQL,binning,0.2579991817474365,855632 +SQL,binning,0.2529940605163574,855632 +SQL,binning,0.23200178146362305,855632 +SQL,binning,0.21999859809875488,855632 +SQL,binning,0.2349991798400879,855632 +SQL,binning,0.2350015640258789,855632 +SQL,binning,0.24399900436401367,855632 +SQL,binning,0.21799921989440918,855632 +SQL,binning,0.2189955711364746,855632 +SQL,binning,0.2340095043182373,855632 +SQL,binning,0.22798752784729004,855632 +SQL,binning,0.2999989986419678,855632 +SQL,binning,0.23201632499694824,855632 +SQL,binning,0.23099613189697266,855632 +SQL,binning,0.2050013542175293,855632 +SQL,binning,0.21700072288513184,855632 +SQL,binning,0.2019963264465332,855632 +SQL,binning,0.22099924087524414,855632 +SQL,binning,0.25800085067749023,855632 +SQL,binning,0.23799753189086914,855632 +SQL,binning,0.2180004119873047,855632 +SQL,binning,0.26999950408935547,855632 +SQL,binning,0.3159980773925781,855632 +SQL,binning,0.2559993267059326,855632 +SQL,binning,0.2340383529663086,855632 +SQL,binning,0.228010892868042,855632 +SQL,binning,0.21796965599060059,855632 +SQL,binning,0.23699736595153809,855632 +SQL,binning,0.1979973316192627,855632 +SQL,binning,0.22599363327026367,855632 +SQL,binning,0.22502636909484863,855632 +SQL,binning,0.22297263145446777,855632 +SQL,binning,0.2259984016418457,855632 +SQL,binning,0.23200297355651855,855632 +SQL,binning,0.21498584747314453,855632 +SQL,binning,0.24000310897827148,855632 +SQL,binning,0.23299837112426758,855632 +SQL,binning,0.22999835014343262,855632 +SQL,binning,0.24399495124816895,855632 +SQL,binning,0.24900031089782715,855632 +SQL,binning,0.23900103569030762,855632 +SQL,binning,0.22200560569763184,855632 +SQL,binning,0.22502446174621582,855632 +SQL,binning,0.21500563621520996,855632 +SQL,binning,0.2150261402130127,855632 +SQL,binning,0.27100443840026855,855632 +SQL,binning,0.28099966049194336,855632 +SQL,binning,0.2700042724609375,855632 +SQL,binning,0.23999619483947754,855632 diff --git a/tests/test_compiler.py b/tests/test_compiler.py index 875e2ea6..9f7b1f83 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -30,6 +30,19 @@ def test_underspecified_no_vis(test_recs): test_recs(df, no_vis_actions) assert len(df.current_vis) == 0 + #test for sql executor + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + + test_recs(sql_df, no_vis_actions) + assert len(sql_df.current_vis) == 0 + + # test only one filter context case. + sql_df.set_intent([lux.Clause(attribute ="origin", filter_op="=", value="USA")]) + test_recs(sql_df, no_vis_actions) + assert len(sql_df.current_vis) == 0 + def test_underspecified_single_vis(test_recs): one_vis_actions = ["Enhance", "Filter", "Generalize"] df = pd.read_csv("lux/data/car.csv") @@ -39,6 +52,16 @@ def test_underspecified_single_vis(test_recs): assert df.current_vis[0].mark == "scatter" for attr in df.current_vis[0]._inferred_intent: assert attr.data_model == "measure" for attr in df.current_vis[0]._inferred_intent: assert attr.data_type == "quantitative" + + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + sql_df.set_intent([lux.Clause(attribute ="milespergal"), lux.Clause(attribute ="weight")]) + test_recs(sql_df, one_vis_actions) + assert len(sql_df.current_vis) == 1 + assert sql_df.current_vis[0].mark == "scatter" + for attr in sql_df.current_vis[0]._inferred_intent: assert attr.data_model == "measure" + for attr in sql_df.current_vis[0]._inferred_intent: assert attr.data_type == "quantitative" # def test_underspecified_vis_collection(test_recs): @@ -80,6 +103,15 @@ def test_set_intent_as_vis(test_recs): df._repr_html_() test_recs(df,["Enhance","Filter","Generalize"]) + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + sql_df._repr_html_() + vis = sql_df.recommendation["Correlation"][0] + sql_df.intent = vis + sql_df._repr_html_() + test_recs(sql_df,["Enhance","Filter","Generalize"]) + @pytest.fixture def test_recs(): def test_recs_function(df, actions): @@ -97,12 +129,31 @@ def test_parse(): df = pd.read_csv("lux/data/car.csv") vlst = VisList([lux.Clause("Origin=?"), lux.Clause("MilesPerGal")],df) assert len(vlst) == 3 + + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vlst = VisList([lux.Clause("origin=?"), lux.Clause(attribute ="milespergal")],sql_df) + assert len(vlst) == 3 + + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vlst = VisList([lux.Clause("origin=?"), lux.Clause("milespergal")],sql_df) + assert len(vlst) == 3 + def test_underspecified_vis_collection_zval(): # check if the number of charts is correct df = pd.read_csv("lux/data/car.csv") vlst = VisList([lux.Clause(attribute ="Origin", filter_op="=", value="?"), lux.Clause(attribute ="MilesPerGal")],df) assert len(vlst) == 3 + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vlst = VisList([lux.Clause(attribute ="origin", filter_op="=", value="?"), lux.Clause(attribute ="milespergal")],sql_df) + assert len(vlst) == 3 + #does not work # df = pd.read_csv("lux/data/car.csv") # vlst = VisList([lux.Clause(attribute = ["Origin","Cylinders"], filter_op="=",value="?"),lux.Clause(attribute = ["Horsepower"]),lux.Clause(attribute = "Weight")],df) @@ -123,6 +174,22 @@ def test_sort_bar(): assert vis.mark == "bar" assert vis._inferred_intent[1].sort == 'ascending' + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute="acceleration",data_model="measure",data_type="quantitative"), + lux.Clause(attribute="origin",data_model="dimension",data_type="nominal")],sql_df) + assert vis.mark == "bar" + assert vis._inferred_intent[1].sort == '' + + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute="acceleration",data_model="measure",data_type="quantitative"), + lux.Clause(attribute="name",data_model="dimension",data_type="nominal")],sql_df) + assert vis.mark == "bar" + assert vis._inferred_intent[1].sort == 'ascending' + def test_specified_vis_collection(): url = 'https://github.com/lux-org/lux-datasets/blob/master/data/cars.csv?raw=true' df = pd.read_csv(url) @@ -147,6 +214,13 @@ def test_specified_channel_enforced_vis_collection(): for vis in visList: check_attribute_on_channel(vis, "MilesPerGal", "x") + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + visList = VisList([lux.Clause(attribute="?"),lux.Clause(attribute="milespergal",channel="x")],sql_df) + for vis in visList: + check_attribute_on_channel(vis, "milespergal", "x") + def test_autoencoding_scatter(): # No channel specified df = pd.read_csv("lux/data/car.csv") @@ -168,6 +242,28 @@ def test_autoencoding_scatter(): with pytest.raises(ValueError): # Should throw error because there should not be columns with the same channel specified df.set_intent([lux.Clause(attribute="MilesPerGal", channel="x"), lux.Clause(attribute="Weight", channel="x")]) + + #test for sql executor + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute="milespergal"), lux.Clause(attribute="weight")],sql_df) + check_attribute_on_channel(vis, "milespergal", "x") + check_attribute_on_channel(vis, "weight", "y") + + # Partial channel specified + vis = Vis([lux.Clause(attribute="milespergal", channel="y"), lux.Clause(attribute="weight")],sql_df) + check_attribute_on_channel(vis, "milespergal", "y") + check_attribute_on_channel(vis, "weight", "x") + + # Full channel specified + vis = Vis([lux.Clause(attribute="milespergal", channel="y"), lux.Clause(attribute="weight", channel="x")],sql_df) + check_attribute_on_channel(vis, "milespergal", "y") + check_attribute_on_channel(vis, "weight", "x") + # Duplicate channel specified + with pytest.raises(ValueError): + # Should throw error because there should not be columns with the same channel specified + sql_df.set_intent([lux.Clause(attribute="milespergal", channel="x"), lux.Clause(attribute="weight", channel="x")]) def test_autoencoding_histogram(): # No channel specified @@ -180,6 +276,18 @@ def test_autoencoding_histogram(): assert vis.get_attr_by_channel("x")[0].attribute == "MilesPerGal" assert vis.get_attr_by_channel("y")[0].attribute == "Record" + # No channel specified + #test for sql executor + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute="milespergal", channel="y")],sql_df) + check_attribute_on_channel(vis, "milespergal", "y") + + vis = Vis([lux.Clause(attribute="milespergal",channel="x")],sql_df) + assert vis.get_attr_by_channel("x")[0].attribute == "milespergal" + assert vis.get_attr_by_channel("y")[0].attribute == "Record" + def test_autoencoding_line_chart(): df = pd.read_csv("lux/data/car.csv") df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype @@ -201,6 +309,28 @@ def test_autoencoding_line_chart(): # Should throw error because there should not be columns with the same channel specified df.set_intent([lux.Clause(attribute="Year", channel="x"), lux.Clause(attribute="Acceleration", channel="x")]) + #test for sql executor + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute="year"), lux.Clause(attribute="acceleration")],sql_df) + check_attribute_on_channel(vis, "year", "x") + check_attribute_on_channel(vis, "acceleration", "y") + + # Partial channel specified + vis = Vis([lux.Clause(attribute="year", channel="y"), lux.Clause(attribute="acceleration")],sql_df) + check_attribute_on_channel(vis, "year", "y") + check_attribute_on_channel(vis, "acceleration", "x") + + # Full channel specified + vis = Vis([lux.Clause(attribute="year", channel="y"), lux.Clause(attribute="acceleration", channel="x")],sql_df) + check_attribute_on_channel(vis, "year", "y") + check_attribute_on_channel(vis, "acceleration", "x") + + with pytest.raises(ValueError): + # Should throw error because there should not be columns with the same channel specified + sql_df.set_intent([lux.Clause(attribute="year", channel="x"), lux.Clause(attribute="acceleration", channel="x")]) + def test_autoencoding_color_line_chart(): df = pd.read_csv("lux/data/car.csv") df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype @@ -210,6 +340,16 @@ def test_autoencoding_color_line_chart(): check_attribute_on_channel(vis, "Acceleration", "y") check_attribute_on_channel(vis, "Origin", "color") + #test for sql executor + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + intent = [lux.Clause(attribute="year"), lux.Clause(attribute="acceleration"), lux.Clause(attribute="origin")] + vis = Vis(intent,sql_df) + check_attribute_on_channel(vis, "year", "x") + check_attribute_on_channel(vis, "acceleration", "y") + check_attribute_on_channel(vis, "origin", "color") + def test_autoencoding_color_scatter_chart(): df = pd.read_csv("lux/data/car.csv") df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype @@ -219,6 +359,16 @@ def test_autoencoding_color_scatter_chart(): vis = Vis([lux.Clause(attribute="Horsepower"), lux.Clause(attribute="Acceleration", channel="color"), lux.Clause(attribute="Origin")],df) check_attribute_on_channel(vis, "Acceleration", "color") + #test for sql executor + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute="horsepower"), lux.Clause(attribute="acceleration"), lux.Clause(attribute="origin")],sql_df) + check_attribute_on_channel(vis, "origin", "color") + + vis = Vis([lux.Clause(attribute="horsepower"), lux.Clause(attribute="acceleration", channel="color"), lux.Clause(attribute="origin")],sql_df) + check_attribute_on_channel(vis, "acceleration", "color") + def test_populate_options(): from lux.processor.Compiler import Compiler df = pd.read_csv("lux/data/car.csv") @@ -237,6 +387,25 @@ def test_populate_options(): col_set.add(clause.attribute) assert list_equal(list(col_set), ['Acceleration', 'Weight', 'Horsepower', 'MilesPerGal', 'Displacement']) + #test for sql executor + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + sql_df.set_intent([lux.Clause(attribute="?"), lux.Clause(attribute="milespergal")]) + col_set = set() + for specOptions in Compiler.populate_wildcard_options(sql_df._intent, sql_df)["attributes"]: + for clause in specOptions: + col_set.add(clause.attribute) + assert list_equal(list(col_set), list(sql_df.columns)) + + sql_df.set_intent([lux.Clause(attribute="?", data_model="measure"), lux.Clause(attribute="milespergal")]) + sql_df._repr_html_() + col_set = set() + for specOptions in Compiler.populate_wildcard_options(sql_df._intent, sql_df)["attributes"]: + for clause in specOptions: + col_set.add(clause.attribute) + assert list_equal(list(col_set), ['acceleration', 'weight', 'horsepower', 'milespergal', 'displacement']) + def test_remove_all_invalid(): df = pd.read_csv("lux/data/car.csv") df["Year"] = pd.to_datetime(df["Year"], format='%Y') @@ -245,6 +414,15 @@ def test_remove_all_invalid(): df._repr_html_() assert len(df.current_vis)==0 + #test for sql executor + connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + # with pytest.warns(UserWarning,match="duplicate attribute specified in the intent"): + sql_df.set_intent([lux.Clause(attribute = "origin", filter_op="=",value="USA"),lux.Clause(attribute = "origin")]) + sql_df._repr_html_() + assert len(sql_df.current_vis)==0 + def list_equal(l1, l2): l1.sort() l2.sort() diff --git a/tests/test_interestingness.py b/tests/test_interestingness.py index 7943e95f..4bc66fcb 100644 --- a/tests/test_interestingness.py +++ b/tests/test_interestingness.py @@ -1,5 +1,5 @@ # 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 @@ -21,143 +21,187 @@ # The following test cases are labelled for vis with def test_interestingness_1_0_0(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - - df.set_intent([lux.Clause(attribute = "Origin")]) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + + df.set_intent([lux.Clause(attribute="Origin")]) df._repr_html_() - #check that top recommended enhance graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended enhance graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Enhance"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Enhance'])): - vis = df.recommendation['Enhance'][f] - if vis.get_attr_by_channel("x")[0].attribute == 'Displacement': + for f in range(0, len(df.recommendation["Enhance"])): + vis = df.recommendation["Enhance"][f] + if vis.get_attr_by_channel("x")[0].attribute == "Displacement": rank1 = f - if vis.get_attr_by_channel("x")[0].attribute == 'Weight': + if vis.get_attr_by_channel("x")[0].attribute == "Weight": rank2 = f - if vis.get_attr_by_channel("x")[0].attribute == 'Acceleration': + if vis.get_attr_by_channel("x")[0].attribute == "Acceleration": rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended filter graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended filter graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Filter"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Filter'])): - vis = df.recommendation['Filter'][f] - if len(vis.get_attr_by_attr_name("Cylinders"))>0: + for f in range(0, len(df.recommendation["Filter"])): + vis = df.recommendation["Filter"][f] + if len(vis.get_attr_by_attr_name("Cylinders")) > 0: if int(vis._inferred_intent[2].value) == 8: rank1 = f if int(vis._inferred_intent[2].value) == 6: - rank2 = f - if '1972' in str(df.recommendation['Filter'][f]._inferred_intent[2].value): - rank3 = f + rank3 = f + if "ford" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): + rank2 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 + + def test_interestingness_1_0_1(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Origin", filter_op="=",value="USA"),lux.Clause(attribute = "Cylinders")]) + df.set_intent( + [ + lux.Clause(attribute="Origin", filter_op="=", value="USA"), + lux.Clause(attribute="Cylinders"), + ] + ) df._repr_html_() assert df.current_vis[0].score == 0 + def test_interestingness_0_1_0(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower")]) + df.set_intent([lux.Clause(attribute="Horsepower")]) df._repr_html_() - #check that top recommended enhance graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended enhance graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Enhance"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Enhance'])): - if df.recommendation['Enhance'][f].mark == 'scatter' and df.recommendation['Enhance'][f]._inferred_intent[1].attribute == 'Weight': + for f in range(0, len(df.recommendation["Enhance"])): + if ( + df.recommendation["Enhance"][f].mark == "scatter" + and df.recommendation["Enhance"][f]._inferred_intent[1].attribute + == "Weight" + ): rank1 = f - if df.recommendation['Enhance'][f].mark == 'scatter' and df.recommendation['Enhance'][f]._inferred_intent[1].attribute == 'Acceleration': + if ( + df.recommendation["Enhance"][f].mark == "scatter" + and df.recommendation["Enhance"][f]._inferred_intent[1].attribute + == "Acceleration" + ): rank2 = f - if df.recommendation['Enhance'][f].mark == 'line' and df.recommendation['Enhance'][f]._inferred_intent[0].attribute == 'Year': + if ( + df.recommendation["Enhance"][f].mark == "line" + and df.recommendation["Enhance"][f]._inferred_intent[0].attribute == "Year" + ): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended filter graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended filter graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Filter"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Filter'])): - if df.recommendation['Filter'][f]._inferred_intent[2].value == 4: + for f in range(0, len(df.recommendation["Filter"])): + if df.recommendation["Filter"][f]._inferred_intent[2].value == 4: rank1 = f - if str(df.recommendation['Filter'][f]._inferred_intent[2].value) == "Europe": + if str(df.recommendation["Filter"][f]._inferred_intent[2].value) == "Europe": rank2 = f - if '1971' in str(df.recommendation['Filter'][f]._inferred_intent[2].value): + if "1970" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 def test_interestingness_0_1_1(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') - - df.set_intent([lux.Clause(attribute = "Origin", filter_op="=",value="?"),lux.Clause(attribute = "MilesPerGal")]) + df["Year"] = pd.to_datetime(df["Year"], format="%Y") + + df.set_intent( + [ + lux.Clause(attribute="Origin", filter_op="=", value="?"), + lux.Clause(attribute="MilesPerGal"), + ] + ) df._repr_html_() - assert interestingness(df.recommendation['Current Vis'][0],df) != None - assert str(df.recommendation['Current Vis'][0]._inferred_intent[2].value) == 'USA' + assert interestingness(df.recommendation["Current Vis"][0], df) != None + assert str(df.recommendation["Current Vis"][0]._inferred_intent[2].value) == "USA" + def test_interestingness_1_1_0(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower"),lux.Clause(attribute = "Year")]) + df.set_intent([lux.Clause(attribute="Horsepower"), lux.Clause(attribute="Year")]) df._repr_html_() - #check that top recommended Enhance graph score is not none (all graphs here have same score) - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended Enhance graph score is not none (all graphs here have same score) + assert interestingness(df.recommendation["Enhance"][0], df) != None - #check that top recommended filter graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended filter graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Filter"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Filter'])): - vis = df.recommendation['Filter'][f] - if len(vis.get_attr_by_attr_name("Cylinders"))>0: + for f in range(0, len(df.recommendation["Filter"])): + vis = df.recommendation["Filter"][f] + if len(vis.get_attr_by_attr_name("Cylinders")) > 0: if int(vis._inferred_intent[2].value) == 6: rank1 = f - if int(vis._inferred_intent[2].value) == 5: - rank3 = f - if len(vis.get_attr_by_attr_name("Origin"))>0: - if str(vis._inferred_intent[2].value) == "Europe": + if int(vis._inferred_intent[2].value) == 8: rank2 = f + if len(vis.get_attr_by_attr_name("Origin")) > 0: + if str(vis._inferred_intent[2].value) == "Europe": + rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended generalize graph score is not none - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended generalize graph score is not none + assert interestingness(df.recommendation["Filter"][0], df) != None + def test_interestingness_1_1_1(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower"), lux.Clause(attribute = "Origin", filter_op="=",value = "USA", bin_size=20)]) + df.set_intent( + [ + lux.Clause(attribute="Horsepower"), + lux.Clause(attribute="Origin", filter_op="=", value="USA", bin_size=20), + ] + ) df._repr_html_() - #check that top recommended Enhance graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended Enhance graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Enhance"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Enhance'])): - if str(df.recommendation['Enhance'][f]._inferred_intent[2].value) == "USA" and str(df.recommendation['Enhance'][f]._inferred_intent[1].attribute) == 'Cylinders': + for f in range(0, len(df.recommendation["Enhance"])): + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].value) == "USA" + and str(df.recommendation["Enhance"][f]._inferred_intent[1].attribute) + == "Cylinders" + ): rank1 = f - if str(df.recommendation['Enhance'][f]._inferred_intent[2].value) == "USA" and str(df.recommendation['Enhance'][f]._inferred_intent[1].attribute) == 'Weight': + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].value) == "USA" + and str(df.recommendation["Enhance"][f]._inferred_intent[1].attribute) + == "Weight" + ): rank2 = f - if str(df.recommendation['Enhance'][f]._inferred_intent[2].value) == "USA" and str(df.recommendation['Enhance'][f]._inferred_intent[1].attribute) == 'Horsepower': + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].value) == "USA" + and str(df.recommendation["Enhance"][f]._inferred_intent[1].attribute) + == "Horsepower" + ): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check for top recommended Filter graph score is not none - assert interestingness(df.recommendation['Filter'][0],df) != None + # check for top recommended Filter graph score is not none + assert interestingness(df.recommendation["Filter"][0], df) != None + def test_interestingness_1_2_0(): from lux.vis.Vis import Vis @@ -165,59 +209,79 @@ def test_interestingness_1_2_0(): from lux.interestingness.interestingness import interestingness df = pd.read_csv("lux/data/car.csv") - y_clause = Clause(attribute = "Name", channel = "y") - color_clause = Clause(attribute = 'Cylinders', channel = "color") + y_clause = Clause(attribute="Name", channel="y") + color_clause = Clause(attribute="Cylinders", channel="color") new_vis = Vis([y_clause, color_clause]) new_vis.refresh_source(df) new_vis - #assert(len(new_vis.data)==color_cardinality*group_by_cardinality) + # assert(len(new_vis.data)==color_cardinality*group_by_cardinality) + + assert interestingness(new_vis, df) < 0.01 - assert(interestingness(new_vis, df)<0.01) def test_interestingness_0_2_0(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower"),lux.Clause(attribute = "Acceleration")]) + df.set_intent( + [lux.Clause(attribute="Horsepower"), lux.Clause(attribute="Acceleration")] + ) df._repr_html_() - #check that top recommended enhance graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Enhance'][0],df) != None + # check that top recommended enhance graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Enhance"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Enhance'])): - if str(df.recommendation['Enhance'][f]._inferred_intent[2].attribute) == "Origin" and str(df.recommendation['Enhance'][f].mark) == 'scatter': + for f in range(0, len(df.recommendation["Enhance"])): + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].attribute) + == "Origin" + and str(df.recommendation["Enhance"][f].mark) == "scatter" + ): rank1 = f - if str(df.recommendation['Enhance'][f]._inferred_intent[2].attribute) == "Displacement" and str(df.recommendation['Enhance'][f].mark) == 'scatter': + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].attribute) + == "Displacement" + and str(df.recommendation["Enhance"][f].mark) == "scatter" + ): rank2 = f - if str(df.recommendation['Enhance'][f]._inferred_intent[2].attribute) == "Year" and str(df.recommendation['Enhance'][f].mark) == 'scatter': + if ( + str(df.recommendation["Enhance"][f]._inferred_intent[2].attribute) == "Year" + and str(df.recommendation["Enhance"][f].mark) == "scatter" + ): rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended filter graph score is not none and that ordering makes intuitive sense - assert interestingness(df.recommendation['Filter'][0],df) != None + # check that top recommended filter graph score is not none and that ordering makes intuitive sense + assert interestingness(df.recommendation["Filter"][0], df) != None rank1 = -1 rank2 = -1 rank3 = -1 - for f in range(0, len(df.recommendation['Filter'])): - if '1973' in str(df.recommendation['Filter'][f]._inferred_intent[2].value): + for f in range(0, len(df.recommendation["Filter"])): + if "1973" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank1 = f - if '1976' in str(df.recommendation['Filter'][f]._inferred_intent[2].value): + if "ford" in str(df.recommendation["Filter"][f]._inferred_intent[2].value): rank2 = f - if str(df.recommendation['Filter'][f]._inferred_intent[2].value) == "Europe": + if str(df.recommendation["Filter"][f]._inferred_intent[2].value) == "USA": rank3 = f assert rank1 < rank2 and rank1 < rank3 and rank2 < rank3 - #check that top recommended Generalize graph score is not none - assert interestingness(df.recommendation['Generalize'][0],df) != None + # check that top recommended Generalize graph score is not none + assert interestingness(df.recommendation["Generalize"][0], df) != None def test_interestingness_0_2_1(): df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') + df["Year"] = pd.to_datetime(df["Year"], format="%Y") - df.set_intent([lux.Clause(attribute = "Horsepower"),lux.Clause(attribute = "MilesPerGal"),lux.Clause(attribute = "Acceleration", filter_op=">",value = 10)]) + df.set_intent( + [ + lux.Clause(attribute="Horsepower"), + lux.Clause(attribute="MilesPerGal"), + lux.Clause(attribute="Acceleration", filter_op=">", value=10), + ] + ) df._repr_html_() - #check that top recommended Generalize graph score is not none - assert interestingness(df.recommendation['Generalize'][0],df) != None \ No newline at end of file + # check that top recommended Generalize graph score is not none + assert interestingness(df.recommendation["Generalize"][0], df) != None \ No newline at end of file From 4046579e4bc6ece5eaadef1394ee0fdcc935f573 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Fri, 6 Nov 2020 18:10:10 -0800 Subject: [PATCH 040/114] Merge with upstream branch, added preliminary benchmarking code --- lux/action/correlation.py | 2 +- lux/action/custom.py | 4 +- lux/action/univariate.py | 4 +- lux/core/frame.py | 131 +- lux/data/upload_car_data.py | 34 +- lux/data/upload_flights_data.py | 45 +- lux/executor/SQLExecutor.py | 606 +- lux/interestingness/interestingness.py | 8 +- sql_benchmarking.csv | 15674 ++++++++++++++++++++++- tests/test_compiler.py | 582 +- tests/test_sql_executor.py | 321 +- 11 files changed, 16412 insertions(+), 999 deletions(-) diff --git a/lux/action/correlation.py b/lux/action/correlation.py index 5d51ba01..d69311ea 100644 --- a/lux/action/correlation.py +++ b/lux/action/correlation.py @@ -54,7 +54,7 @@ def correlation(ldf: LuxDataFrame, ignore_transpose: bool = True): } ignore_rec_flag = False if ( - len(ldf) < 5 + ldf.length < 5 ): # Doesn't make sense to compute correlation if less than 4 data values ignore_rec_flag = True # Then use the data populated in the vis list to compute score diff --git a/lux/action/custom.py b/lux/action/custom.py index b43d08ed..ca4c0571 100644 --- a/lux/action/custom.py +++ b/lux/action/custom.py @@ -42,8 +42,8 @@ def custom(ldf): vlist = ldf.current_vis ldf.executor.execute(vlist, ldf) - for vis in vlist: - vis.score = interestingness(vis,ldf) + for vis in vlist: + vis.score = interestingness(vis, ldf) # ldf.clear_intent() vlist.sort(remove_invalid=True) return recommendation diff --git a/lux/action/univariate.py b/lux/action/univariate.py index 4eb0157e..836f14b0 100644 --- a/lux/action/univariate.py +++ b/lux/action/univariate.py @@ -59,7 +59,7 @@ def univariate(ldf, *args): "description": "Show univariate histograms of

quantitative

attributes.", } if ( - len(ldf) < 5 + ldf.length < 5 ): # Doesn't make sense to generate a histogram if there is less than 5 datapoints (pre-aggregated) ignore_rec_flag = True elif data_type_constraint == "nominal": @@ -77,7 +77,7 @@ def univariate(ldf, *args): "description": "Show trends over

time-related

attributes.", } if ( - len(ldf) < 3 + ldf.length < 3 ): # Doesn't make sense to generate a line chart if there is less than 3 datapoints (pre-aggregated) ignore_rec_flag = True if ignore_rec_flag: diff --git a/lux/core/frame.py b/lux/core/frame.py index 5776702e..af3ba8a2 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -38,6 +38,7 @@ class LuxDataFrame(pd.DataFrame): "data_type", "data_model_lookup", "data_model", + "length", "unique_values", "cardinality", "_rec_info", @@ -79,6 +80,7 @@ def __init__(self, *args, **kw): self.data_type = None self.data_model_lookup = None self.data_model = None + self.length = None self.unique_values = None self.cardinality = None self._min_max = None @@ -107,7 +109,7 @@ def maintain_metadata(self): not hasattr(self, "_metadata_fresh") or not self._metadata_fresh ): # Check that metadata has not yet been computed if ( - len(self) > 0 + len(self) > 0 or self.executor_type == "SQL" ): # only compute metadata information if the dataframe is non-empty self.executor.compute_stats(self) self.executor.compute_dataset_metadata(self) @@ -117,7 +119,7 @@ def maintain_metadata(self): def expire_recs(self): self._recs_fresh = False self.recommendation = {} - self.current_vis = None + self.current_vis = [] self._widget = None self._rec_info = None self._sampled = None @@ -129,6 +131,7 @@ def expire_metadata(self): self.data_type = None self.data_model_lookup = None self.data_model = None + self.length = None self.unique_values = None self.cardinality = None self._min_max = None @@ -162,13 +165,13 @@ 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 not_int_index_flag = self.index.dtype != "int64" - small_df_flag = len(self) < 100 + small_df_flag = len(self) < 100 and self.executor_type == "Pandas" self.pre_aggregated = ( is_multi_index_flag or not_int_index_flag ) and small_df_flag if "Number of Records" in self.columns: self.pre_aggregated = True - very_small_df_flag = len(self) <= 10 + very_small_df_flag = len(self) <= 10 and self.executor_type == "Pandas" if very_small_df_flag: self.pre_aggregated = True @@ -184,7 +187,7 @@ def set_executor_type(self, exe): import psycopg2 from lux.executor.SQLExecutor import SQLExecutor - self.executor = SQLExecutor + self.executor = SQLExecutor() else: from lux.executor.PandasExecutor import PandasExecutor @@ -275,8 +278,8 @@ def _parse_validate_compile_intent(self): from lux.processor.Validator import Validator self._intent = Parser.parse(self._intent) - Validator.validate_intent(self._intent, self) self.maintain_metadata() + Validator.validate_intent(self._intent, self) from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) @@ -333,119 +336,8 @@ def __repr__(self): def set_SQL_connection(self, connection, t_name): self.SQLconnection = connection self.table_name = t_name - self.compute_SQL_dataset_metadata() self.set_executor_type("SQL") - - def compute_SQL_dataset_metadata(self): - self.get_SQL_attributes() - for attr in list(self.columns): - self[attr] = None - self.data_type_lookup = {} - self.data_type = {} - #####NOTE: since we aren't expecting users to do much data processing with the SQL database, should we just keep this - ##### in the initialization and do it just once - self.compute_SQL_data_type() - self.compute_SQL_stats() - self.data_model_lookup = {} - self.data_model = {} - self.compute_data_model() - - def compute_SQL_stats(self): - # precompute statistics - self.unique_values = {} - self._min_max = {} - - self.get_SQL_unique_values() - # self.get_SQL_cardinality() - for attribute in self.columns: - if self.data_type_lookup[attribute] == "quantitative": - self._min_max[attribute] = ( - self[attribute].min(), - self[attribute].max(), - ) - - def get_SQL_attributes(self): - if "." in self.table_name: - table_name = self.table_name[self.table_name.index(".") + 1 :] - else: - table_name = self.table_name - attr_query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '{}'".format( - table_name - ) - attributes = list(pd.read_sql(attr_query, self.SQLconnection)["column_name"]) - for attr in attributes: - self[attr] = None - - def get_SQL_cardinality(self): - cardinality = {} - for attr in list(self.columns): - card_query = pd.read_sql( - "SELECT Count(Distinct({})) FROM {}".format(attr, self.table_name), - self.SQLconnection, - ) - cardinality[attr] = list(card_query["count"])[0] - self.cardinality = cardinality - - def get_SQL_unique_values(self): - unique_vals = {} - for attr in list(self.columns): - unique_query = pd.read_sql( - "SELECT Distinct({}) FROM {}".format(attr, self.table_name), - self.SQLconnection, - ) - unique_vals[attr] = list(unique_query[attr]) - self.unique_values = unique_vals - - def compute_SQL_data_type(self): - data_type_lookup = {} - sql_dtypes = {} - self.get_SQL_cardinality() - if "." in self.table_name: - table_name = self.table_name[self.table_name.index(".") + 1 :] - else: - table_name = self.table_name - # get the data types of the attributes in the SQL table - for attr in list(self.columns): - datatype_query = "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{}' AND COLUMN_NAME = '{}'".format( - table_name, attr - ) - datatype = list( - pd.read_sql(datatype_query, self.SQLconnection)["data_type"] - )[0] - sql_dtypes[attr] = datatype - - data_type = {"quantitative": [], "nominal": [], "temporal": []} - for attr in list(self.columns): - if str(attr).lower() in ["month", "year"]: - data_type_lookup[attr] = "temporal" - data_type["temporal"].append(attr) - elif sql_dtypes[attr] in [ - "character", - "character varying", - "boolean", - "uuid", - "text", - ]: - data_type_lookup[attr] = "nominal" - data_type["nominal"].append(attr) - elif sql_dtypes[attr] in [ - "integer", - "real", - "smallint", - "smallserial", - "serial", - ]: - if self.cardinality[attr] < 13: - data_type_lookup[attr] = "nominal" - data_type["nominal"].append(attr) - else: - data_type_lookup[attr] = "quantitative" - data_type["quantitative"].append(attr) - elif "time" in sql_dtypes[attr] or "date" in sql_dtypes[attr]: - data_type_lookup[attr] = "temporal" - data_type["temporal"].append(attr) - self.data_type_lookup = data_type_lookup - self.data_type = data_type + self.executor.compute_dataset_metadata(self) def _append_rec(self, rec_infolist, recommendations: Dict): if ( @@ -688,8 +580,7 @@ def _repr_html_(self): ) display(self.display_pandas()) return - - if len(self) <= 0: + if len(self) <= 0 and self.executor_type == "Pandas": warnings.warn( "\nLux can not operate on an empty dataframe.\nPlease check your input again.\n", stacklevel=2, diff --git a/lux/data/upload_car_data.py b/lux/data/upload_car_data.py index 289f7003..1494424c 100644 --- a/lux/data/upload_car_data.py +++ b/lux/data/upload_car_data.py @@ -3,13 +3,17 @@ import csv import psycopg2 + conn = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") cur = conn.cursor() -cur.execute(""" +cur.execute( + """ DROP TABLE IF EXISTS car - """) -#create car table in postgres database -cur.execute(""" + """ +) +# create car table in postgres database +cur.execute( + """ CREATE TABLE car( name text, milespergal numeric, @@ -21,17 +25,15 @@ year integer, origin text ) -""") +""" +) -#open car.csv and read data into database -with open('lux/data/car.csv', 'r') as f: - reader = csv.reader(f) - next(reader) # Skip the header row. - i = 0 - for row in reader: - cur.execute( - "INSERT INTO car VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)", - row - ) +# open car.csv and read data into database +with open("lux/data/car.csv", "r") as f: + reader = csv.reader(f) + next(reader) # Skip the header row. + i = 0 + for row in reader: + cur.execute("INSERT INTO car VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)", row) -conn.commit() \ No newline at end of file +conn.commit() diff --git a/lux/data/upload_flights_data.py b/lux/data/upload_flights_data.py index 0ea65c63..8dd7f8d4 100644 --- a/lux/data/upload_flights_data.py +++ b/lux/data/upload_flights_data.py @@ -4,14 +4,18 @@ import csv import psycopg2 + conn = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") cur = conn.cursor() -cur.execute(""" +cur.execute( + """ DROP TABLE IF EXISTS flights - """) + """ +) -#create flights table in postgres database -cur.execute(""" +# create flights table in postgres database +cur.execute( + """ CREATE TABLE flights( year integer, month text, @@ -25,21 +29,22 @@ weatherdelay integer, distance integer ) -""") +""" +) -#open flights.csv and read data into database -with open('flights.csv', 'r') as f: - reader = csv.reader(f) - next(reader) # Skip the header row. - i = 0 - for row in reader: - if i%50000 == 0: - print(i) - i+=1 - #print(row) - cur.execute( - "INSERT INTO flights VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", - row - ) +# open flights.csv and read data into database +with open("flights.csv", "r") as f: + reader = csv.reader(f) + next(reader) # Skip the header row. + i = 0 + for row in reader: + if i % 50000 == 0: + print(i) + i += 1 + # print(row) + cur.execute( + "INSERT INTO flights VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", + row, + ) -conn.commit() \ No newline at end of file +conn.commit() diff --git a/lux/executor/SQLExecutor.py b/lux/executor/SQLExecutor.py index 516e033a..40424887 100644 --- a/lux/executor/SQLExecutor.py +++ b/lux/executor/SQLExecutor.py @@ -1,17 +1,3 @@ -# 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. - import pandas from lux.vis.VisList import VisList from lux.vis.Vis import Vis @@ -22,7 +8,7 @@ import math -#for benchmarking +# for benchmarking import time @@ -41,30 +27,36 @@ def __repr__(self): return f"" @staticmethod - def execute(vislist: VisList, ldf: LuxDataFrame): - import pandas as pd - + def execute(view_collection: VisList, ldf: LuxDataFrame): """ - Given a VisList, fetch the data required to render the vis + Given a VisList, fetch the data required to render the view 1) Apply filters 2) Retreive relevant attribute 3) return a DataFrame with relevant results """ - for vis in vislist: + + for view in view_collection: # Select relevant data based on attribute information attributes = set([]) - for clause in vis._inferred_intent: + for clause in view._inferred_intent: if clause.attribute: - if clause.attribute == "Record": + if clause.attribute != "Record": attributes.add(clause.attribute) - # else: - attributes.add(clause.attribute) - if vis.mark not in ["bar", "line", "histogram"]: - where_clause, filterVars = SQLExecutor.execute_filter(vis) + if view.mark not in ["bar", "line", "histogram"]: + start = time.time() + where_clause, filterVars = SQLExecutor.execute_filter(view) + + length_query = pandas.read_sql( + "SELECT COUNT(*) as length FROM {} {}".format( + ldf.table_name, where_clause + ), + ldf.SQLconnection, + ) + required_variables = attributes | set(filterVars) required_variables = ",".join(required_variables) row_count = list( - pd.read_sql( + pandas.read_sql( "SELECT COUNT(*) FROM {} {}".format( ldf.table_name, where_clause ), @@ -76,40 +68,68 @@ def execute(vislist: VisList, ldf: LuxDataFrame): required_variables, ldf.table_name, where_clause ) else: - query = "SELECT {} FROM {} {}".format(required_variables, ldf.table_name, where_clause) + query = "SELECT {} FROM {} {}".format( + required_variables, ldf.table_name, where_clause + ) data = pandas.read_sql(query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(data) - view._vis_data.length = list(length_query['length'])[0] + view._vis_data.length = list(length_query["length"])[0] end = time.time() - #append benchmark data to file - benchmark_data = {'executor_name':['SQL'], 'query_action':['scatter'], 'time':[end-start], 'length':[ldf.length]} - benchmark_df = pandas.DataFrame(data = benchmark_data) - benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) - #print("time to collect scatterplot data", end-start) - if (view.mark =="bar" or view.mark =="line"): + # append benchmark data to file + benchmark_data = { + "executor_name": ["SQL"], + "query_action": ["scatter"], + "time": [end - start], + "length": [ldf.length], + } + benchmark_df = pandas.DataFrame(data=benchmark_data) + benchmark_df.to_csv( + "C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv", + mode="a", + header=False, + index=False, + ) + if view.mark == "bar" or view.mark == "line": start = time.time() SQLExecutor.execute_aggregate(view, ldf) end = time.time() - #append benchmark data to file - benchmark_data = {'executor_name':['SQL'], 'query_action':['bar/line'], 'time':[end-start], 'length':[ldf.length]} - benchmark_df = pandas.DataFrame(data = benchmark_data) - benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) - #print("time to collect bar/line data", end-start) - elif (view.mark =="histogram"): + # append benchmark data to file + benchmark_data = { + "executor_name": ["SQL"], + "query_action": ["bar/line"], + "time": [end - start], + "length": [ldf.length], + } + benchmark_df = pandas.DataFrame(data=benchmark_data) + benchmark_df.to_csv( + "C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv", + mode="a", + header=False, + index=False, + ) + elif view.mark == "histogram": start = time.time() SQLExecutor.execute_binning(view, ldf) end = time.time() - #append benchmark data to file - benchmark_data = {'executor_name':['SQL'], 'query_action':['binning'], 'time':[end-start], 'length':[ldf.length]} - benchmark_df = pandas.DataFrame(data = benchmark_data) - benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) - #print("time to collect histogram data", end-start) + # append benchmark data to file + benchmark_data = { + "executor_name": ["SQL"], + "query_action": ["binning"], + "time": [end - start], + "length": [ldf.length], + } + benchmark_df = pandas.DataFrame(data=benchmark_data) + benchmark_df.to_csv( + "C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv", + mode="a", + header=False, + index=False, + ) @staticmethod - def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): - ''' + def execute_aggregate(view: Vis, ldf: LuxDataFrame, isFiltered=True): + """ Aggregate data points on an axis for bar or line charts - Parameters ---------- vis: lux.Vis @@ -118,197 +138,326 @@ def execute_aggregate(view:Vis, ldf:LuxDataFrame, isFiltered = True): LuxDataFrame with specified intent. isFiltered: boolean boolean that represents whether a vis has had a filter applied - Returns ------- None - ''' + """ x_attr = view.get_attr_by_channel("x")[0] y_attr = view.get_attr_by_channel("y")[0] has_color = False - groupby_attr ="" - measure_attr ="" - if (x_attr.aggregation is None or y_attr.aggregation is None): + groupby_attr = "" + measure_attr = "" + if x_attr.aggregation is None or y_attr.aggregation is None: return - if (y_attr.aggregation!=""): + if y_attr.aggregation != "": + groupby_attr = x_attr measure_attr = y_attr agg_func = y_attr.aggregation if x_attr.aggregation != "": groupby_attr = y_attr measure_attr = x_attr agg_func = x_attr.aggregation - if (groupby_attr.attribute in ldf.unique_values.keys()): + if groupby_attr.attribute in ldf.unique_values.keys(): attr_unique_vals = ldf.unique_values[groupby_attr.attribute] - #checks if color is specified in the Vis + # checks if color is specified in the Vis if len(view.get_attr_by_channel("color")) == 1: color_attr = view.get_attr_by_channel("color")[0] color_attr_vals = ldf.unique_values[color_attr.attribute] color_cardinality = len(color_attr_vals) - #NOTE: might want to have a check somewhere to not use categorical variables with greater than some number of categories as a Color variable---------------- + # NOTE: might want to have a check somewhere to not use categorical variables with greater than some number of categories as a Color variable---------------- has_color = True else: color_cardinality = 1 - if (measure_attr!=""): - #barchart case, need count data for each group - if (measure_attr.attribute=="Record"): + if measure_attr != "": + # barchart case, need count data for each group + if measure_attr.attribute == "Record": where_clause, filterVars = SQLExecutor.execute_filter(view) - length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) + length_query = pandas.read_sql( + "SELECT COUNT(*) as length FROM {} {}".format( + ldf.table_name, where_clause + ), + ldf.SQLconnection, + ) if has_color: - count_query = "SELECT {}, {}, COUNT({}) FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, groupby_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) + count_query = ( + "SELECT {}, {}, COUNT({}) FROM {} {} GROUP BY {}, {}".format( + groupby_attr.attribute, + color_attr.attribute, + groupby_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + color_attr.attribute, + ) + ) view._vis_data = pandas.read_sql(count_query, ldf.SQLconnection) - view._vis_data = view._vis_data.rename(columns={"count":"Record"}) + view._vis_data = view._vis_data.rename(columns={"count": "Record"}) view._vis_data = utils.pandas_to_lux(view._vis_data) else: - count_query = "SELECT {}, COUNT({}) FROM {} {} GROUP BY {}".format(groupby_attr.attribute, groupby_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + count_query = "SELECT {}, COUNT({}) FROM {} {} GROUP BY {}".format( + groupby_attr.attribute, + groupby_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + ) view._vis_data = pandas.read_sql(count_query, ldf.SQLconnection) - view._vis_data = view._vis_data.rename(columns={"count":"Record"}) + view._vis_data = view._vis_data.rename(columns={"count": "Record"}) view._vis_data = utils.pandas_to_lux(view._vis_data) - view._vis_data.length = list(length_query['length'])[0] - #aggregate barchart case, need aggregate data for each group + view._vis_data.length = list(length_query["length"])[0] + # aggregate barchart case, need aggregate data for each group else: where_clause, filterVars = SQLExecutor.execute_filter(view) - length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) + length_query = pandas.read_sql( + "SELECT COUNT(*) as length FROM {} {}".format( + ldf.table_name, where_clause + ), + ldf.SQLconnection, + ) if has_color: if agg_func == "mean": - mean_query = "SELECT {}, {}, AVG({}) as {} FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) + mean_query = "SELECT {}, {}, AVG({}) as {} FROM {} {} GROUP BY {}, {}".format( + groupby_attr.attribute, + color_attr.attribute, + measure_attr.attribute, + measure_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + color_attr.attribute, + ) view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(view._vis_data) if agg_func == "sum": - mean_query = "SELECT {}, {}, SUM({}) as {} FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) + mean_query = "SELECT {}, {}, SUM({}) as {} FROM {} {} GROUP BY {}, {}".format( + groupby_attr.attribute, + color_attr.attribute, + measure_attr.attribute, + measure_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + color_attr.attribute, + ) view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(view._vis_data) if agg_func == "max": - mean_query = "SELECT {}, {}, MAX({}) as {} FROM {} {} GROUP BY {}, {}".format(groupby_attr.attribute, color_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute, color_attr.attribute) + mean_query = "SELECT {}, {}, MAX({}) as {} FROM {} {} GROUP BY {}, {}".format( + groupby_attr.attribute, + color_attr.attribute, + measure_attr.attribute, + measure_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + color_attr.attribute, + ) view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(view._vis_data) else: if agg_func == "mean": - mean_query = "SELECT {}, AVG({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + mean_query = ( + "SELECT {}, AVG({}) as {} FROM {} {} GROUP BY {}".format( + groupby_attr.attribute, + measure_attr.attribute, + measure_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + ) + ) view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(view._vis_data) if agg_func == "sum": - mean_query = "SELECT {}, SUM({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + mean_query = ( + "SELECT {}, SUM({}) as {} FROM {} {} GROUP BY {}".format( + groupby_attr.attribute, + measure_attr.attribute, + measure_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + ) + ) view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(view._vis_data) if agg_func == "max": - mean_query = "SELECT {}, MAX({}) as {} FROM {} {} GROUP BY {}".format(groupby_attr.attribute, measure_attr.attribute, measure_attr.attribute, ldf.table_name, where_clause, groupby_attr.attribute) + mean_query = ( + "SELECT {}, MAX({}) as {} FROM {} {} GROUP BY {}".format( + groupby_attr.attribute, + measure_attr.attribute, + measure_attr.attribute, + ldf.table_name, + where_clause, + groupby_attr.attribute, + ) + ) view._vis_data = pandas.read_sql(mean_query, ldf.SQLconnection) view._vis_data = utils.pandas_to_lux(view._vis_data) result_vals = list(view._vis_data[groupby_attr.attribute]) - #create existing group by attribute combinations if color is specified - #this is needed to check what combinations of group_by_attr and color_attr values have a non-zero number of elements in them + # create existing group by attribute combinations if color is specified + # this is needed to check what combinations of group_by_attr and color_attr values have a non-zero number of elements in them if has_color: res_color_combi_vals = [] result_color_vals = list(view._vis_data[color_attr.attribute]) for i in range(0, len(result_vals)): res_color_combi_vals.append([result_vals[i], result_color_vals[i]]) # For filtered aggregation that have missing groupby-attribute values, set these aggregated value as 0, since no datapoints - if (isFiltered or has_color and attr_unique_vals): + if isFiltered or has_color and attr_unique_vals: N_unique_vals = len(attr_unique_vals) - if (len(result_vals) != N_unique_vals*color_cardinality): + if len(result_vals) != N_unique_vals * color_cardinality: columns = view._vis_data.columns if has_color: - df = pandas.DataFrame({columns[0]: attr_unique_vals*color_cardinality, columns[1]: pandas.Series(color_attr_vals).repeat(N_unique_vals)}) - view._vis_data = view._vis_data.merge(df, on=[columns[0],columns[1]], how='right', suffixes=['', '_right']) + df = pandas.DataFrame( + { + columns[0]: attr_unique_vals * color_cardinality, + columns[1]: pandas.Series(color_attr_vals).repeat( + N_unique_vals + ), + } + ) + view._vis_data = view._vis_data.merge( + df, + on=[columns[0], columns[1]], + how="right", + suffixes=["", "_right"], + ) for col in columns[2:]: - view._vis_data[col] = view._vis_data[col].fillna(0) #Triggers __setitem__ - assert len(list(view._vis_data[groupby_attr.attribute])) == N_unique_vals*len(color_attr_vals), f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute, color_attr.attribute}`." - view._vis_data = view._vis_data.iloc[:,:3] # Keep only the three relevant columns not the *_right columns resulting from merge + view._vis_data[col] = view._vis_data[col].fillna( + 0 + ) # Triggers __setitem__ + assert len( + list(view._vis_data[groupby_attr.attribute]) + ) == N_unique_vals * len( + color_attr_vals + ), f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute, color_attr.attribute}`." + view._vis_data = view._vis_data.iloc[ + :, :3 + ] # Keep only the three relevant columns not the *_right columns resulting from merge else: df = pandas.DataFrame({columns[0]: attr_unique_vals}) - - view._vis_data = view._vis_data.merge(df, on=columns[0], how='right', suffixes=['', '_right']) + + view._vis_data = view._vis_data.merge( + df, on=columns[0], how="right", suffixes=["", "_right"] + ) for col in columns[1:]: view._vis_data[col] = view._vis_data[col].fillna(0) - assert len(list(view._vis_data[groupby_attr.attribute])) == N_unique_vals, f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute}`." - view._vis_data = view._vis_data.sort_values(by=groupby_attr.attribute, ascending=True) + assert ( + len(list(view._vis_data[groupby_attr.attribute])) + == N_unique_vals + ), f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute}`." + view._vis_data = view._vis_data.sort_values( + by=groupby_attr.attribute, ascending=True + ) view._vis_data = view._vis_data.reset_index() view._vis_data = view._vis_data.drop(columns="index") - view._vis_data.length = list(length_query['length'])[0] + view._vis_data.length = list(length_query["length"])[0] @staticmethod - def execute_binning(view:Vis, ldf:LuxDataFrame): - ''' + def execute_binning(view: Vis, ldf: LuxDataFrame): + """ Binning of data points for generating histograms - Parameters ---------- vis: lux.Vis lux.Vis object that represents a visualization ldf : lux.core.frame LuxDataFrame with specified intent. - Returns ------- None - ''' + """ import numpy as np - bin_attribute = list(filter(lambda x: x.bin_size!=0,view._inferred_intent))[0] + + bin_attribute = list(filter(lambda x: x.bin_size != 0, view._inferred_intent))[ + 0 + ] num_bins = bin_attribute.bin_size attr_min = min(ldf.unique_values[bin_attribute.attribute]) attr_max = max(ldf.unique_values[bin_attribute.attribute]) attr_type = type(ldf.unique_values[bin_attribute.attribute][0]) - #get filters if available + # get filters if available where_clause, filterVars = SQLExecutor.execute_filter(view) - length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), ldf.SQLconnection) - #need to calculate the bin edges before querying for the relevant data - bin_width = (attr_max-attr_min)/num_bins + length_query = pandas.read_sql( + "SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), + ldf.SQLconnection, + ) + # need to calculate the bin edges before querying for the relevant data + bin_width = (attr_max - attr_min) / num_bins upper_edges = [] for e in range(1, num_bins): - curr_edge = attr_min + e*bin_width + curr_edge = attr_min + e * bin_width if attr_type == int: upper_edges.append(str(math.ceil(curr_edge))) else: upper_edges.append(str(curr_edge)) upper_edges = ",".join(upper_edges) view_filter, filter_vars = SQLExecutor.execute_filter(view) - bin_count_query = "SELECT width_bucket, COUNT(width_bucket) FROM (SELECT width_bucket({}, '{}') FROM {} {}) as Buckets GROUP BY width_bucket ORDER BY width_bucket".format(bin_attribute.attribute, '{'+upper_edges+'}', ldf.table_name, where_clause) + bin_count_query = "SELECT width_bucket, COUNT(width_bucket) FROM (SELECT width_bucket({}, '{}') FROM {} {}) as Buckets GROUP BY width_bucket ORDER BY width_bucket".format( + bin_attribute.attribute, + "{" + upper_edges + "}", + ldf.table_name, + where_clause, + ) bin_count_data = pandas.read_sql(bin_count_query, ldf.SQLconnection) - #counts,binEdges = np.histogram(ldf[bin_attribute.attribute],bins=bin_attribute.bin_size) - #binEdges of size N+1, so need to compute binCenter as the bin location + # counts,binEdges = np.histogram(ldf[bin_attribute.attribute],bins=bin_attribute.bin_size) + # binEdges of size N+1, so need to compute binCenter as the bin location upper_edges = [float(i) for i in upper_edges.split(",")] if attr_type == int: - bin_centers = np.array([math.ceil((attr_min+attr_min+bin_width)/2)]) + bin_centers = np.array([math.ceil((attr_min + attr_min + bin_width) / 2)]) else: - bin_centers = np.array([(attr_min+attr_min+bin_width)/2]) - bin_centers = np.append(bin_centers, np.mean(np.vstack([upper_edges[0:-1],upper_edges[1:]]), axis=0)) + bin_centers = np.array([(attr_min + attr_min + bin_width) / 2]) + bin_centers = np.append( + bin_centers, + np.mean(np.vstack([upper_edges[0:-1], upper_edges[1:]]), axis=0), + ) if attr_type == int: - bin_centers = np.append(bin_centers, math.ceil((upper_edges[len(upper_edges)-1]+attr_max)/2)) + bin_centers = np.append( + bin_centers, + math.ceil((upper_edges[len(upper_edges) - 1] + attr_max) / 2), + ) else: - bin_centers = np.append(bin_centers, (upper_edges[len(upper_edges)-1]+attr_max)/2) + bin_centers = np.append( + bin_centers, (upper_edges[len(upper_edges) - 1] + attr_max) / 2 + ) if len(bin_centers) > len(bin_count_data): - bucket_lables = bin_count_data['width_bucket'].unique() - for i in range(0,len(bin_centers)): + bucket_lables = bin_count_data["width_bucket"].unique() + for i in range(0, len(bin_centers)): if i not in bucket_lables: - bin_count_data = bin_count_data.append(pandas.DataFrame([[i,0]], columns = bin_count_data.columns)) - view._vis_data = pandas.DataFrame(np.array([bin_centers,list(bin_count_data['count'])]).T,columns=[bin_attribute.attribute, "Number of Records"]) + bin_count_data = bin_count_data.append( + pandas.DataFrame([[i, 0]], columns=bin_count_data.columns) + ) + view._vis_data = pandas.DataFrame( + np.array([bin_centers, list(bin_count_data["count"])]).T, + columns=[bin_attribute.attribute, "Number of Records"], + ) view._vis_data = utils.pandas_to_lux(view.data) - view._vis_data.length = list(length_query['length'])[0] - + view._vis_data.length = list(length_query["length"])[0] + @staticmethod - #takes in a view and returns an appropriate SQL WHERE clause that based on the filters specified in the view's _inferred_intent - def execute_filter(view:Vis): + # takes in a view and returns an appropriate SQL WHERE clause that based on the filters specified in the view's _inferred_intent + def execute_filter(view: Vis): """ Helper function for converting a filter specification to a SQL where clause - + Parameters ---------- vis: lux.Vis lux.Vis object that represents a visualization - + Returns ------- list: list of lists List containing the list of components to be used in the SQL where clause, and the list of variables used in this clause - """ + """ where_clause = [] filters = utils.get_filter_specs(view._inferred_intent) filter_vars = [] @@ -331,20 +480,19 @@ def execute_filter(view:Vis): return ("", []) else: where_clause = " ".join(where_clause) - return(where_clause, filter_vars) - + return (where_clause, filter_vars) ####################################################### ########## Metadata, type, model schema ########### ####################################################### - def compute_dataset_metadata(self, ldf:LuxDataFrame): + def compute_dataset_metadata(self, ldf: LuxDataFrame): self.get_SQL_attributes(ldf) for attr in list(ldf.columns): ldf[attr] = None ldf.data_type_lookup = {} ldf.data_type = {} - #####NOTE: since we aren't expecting users to do much data processing with the SQL database, should we just keep this + #####NOTE: since we aren't expecting users to do much data processing with the SQL database, should we just keep this ##### in the initialization and do it just once self.compute_data_type(ldf) self.compute_stats(ldf) @@ -352,110 +500,214 @@ def compute_dataset_metadata(self, ldf:LuxDataFrame): ldf.data_model = {} self.compute_data_model(ldf) - def get_SQL_attributes(self, ldf:LuxDataFrame): + def get_SQL_attributes(self, ldf: LuxDataFrame): start = time.time() if "." in ldf.table_name: - table_name = ldf.table_name[self.table_name.index(".")+1:] + table_name = ldf.table_name[self.table_name.index(".") + 1 :] else: table_name = ldf.table_name - attr_query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '{}'".format(table_name) - attributes = list(pandas.read_sql(attr_query, ldf.SQLconnection)['column_name']) + attr_query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '{}'".format( + table_name + ) + attributes = list(pandas.read_sql(attr_query, ldf.SQLconnection)["column_name"]) for attr in attributes: ldf[attr] = None end = time.time() - #append benchmark data to file - benchmark_data = {'executor_name':['SQL'], 'query_action':['get_attributes'], 'time':[end-start], 'length':[ldf.length]} - benchmark_df = pandas.DataFrame(data = benchmark_data) - benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) - - def compute_stats(self, ldf:LuxDataFrame): + # append benchmark data to file + benchmark_data = { + "executor_name": ["SQL"], + "query_action": ["get_attributes"], + "time": [end - start], + "length": [ldf.length], + } + benchmark_df = pandas.DataFrame(data=benchmark_data) + benchmark_df.to_csv( + "C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv", + mode="a", + header=False, + index=False, + ) + + def compute_stats(self, ldf: LuxDataFrame): # precompute statistics ldf.unique_values = {} ldf._min_max = {} start = time.time() - length_query = pandas.read_sql("SELECT COUNT(*) as length FROM {}".format(ldf.table_name), ldf.SQLconnection) + length_query = pandas.read_sql( + "SELECT COUNT(*) as length FROM {}".format(ldf.table_name), + ldf.SQLconnection, + ) end = time.time() - #append benchmark data to file - benchmark_data = {'executor_name':['SQL'], 'query_action':['data_length'], 'time':[end-start], 'length':[ldf.length]} - benchmark_df = pandas.DataFrame(data = benchmark_data) - ldf.length = list(length_query['length'])[0] - benchmark_df.to_pandas().to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + # append benchmark data to file + benchmark_data = { + "executor_name": ["SQL"], + "query_action": ["data_length"], + "time": [end - start], + "length": [ldf.length], + } + benchmark_df = pandas.DataFrame(data=benchmark_data) + ldf.length = list(length_query["length"])[0] + benchmark_df.to_pandas().to_csv( + "C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv", + mode="a", + header=False, + index=False, + ) self.get_unique_values(ldf) - #ldf.get_cardinality() + # ldf.get_cardinality() for attribute in ldf.columns: - if ldf.data_type_lookup[attribute] == 'quantitative': + if ldf.data_type_lookup[attribute] == "quantitative": start = time.time() - min_max_query = pandas.read_sql("SELECT MIN({}) as min, MAX({}) as max FROM {}".format(attribute, attribute, ldf.table_name), ldf.SQLconnection) - end=time.time() - ldf._min_max[attribute] = (list(min_max_query["min"])[0], list(min_max_query['max'])[0]) - #append benchmark data to file - benchmark_data = {'executor_name':['SQL'], 'query_action':['min_max'], 'time':[end-start], 'length':[ldf.length]} - benchmark_df = pandas.DataFrame(data = benchmark_data) - benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) - - def get_cardinality(self, ldf:LuxDataFrame): + min_max_query = pandas.read_sql( + "SELECT MIN({}) as min, MAX({}) as max FROM {}".format( + attribute, attribute, ldf.table_name + ), + ldf.SQLconnection, + ) + end = time.time() + ldf._min_max[attribute] = ( + list(min_max_query["min"])[0], + list(min_max_query["max"])[0], + ) + # append benchmark data to file + benchmark_data = { + "executor_name": ["SQL"], + "query_action": ["min_max"], + "time": [end - start], + "length": [ldf.length], + } + benchmark_df = pandas.DataFrame(data=benchmark_data) + benchmark_df.to_csv( + "C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv", + mode="a", + header=False, + index=False, + ) + + def get_cardinality(self, ldf: LuxDataFrame): cardinality = {} for attr in list(ldf.columns): start = time.time() - card_query = pandas.read_sql("SELECT Count(Distinct({})) FROM {}".format(attr, ldf.table_name), ldf.SQLconnection) + card_query = pandas.read_sql( + "SELECT Count(Distinct({})) FROM {}".format(attr, ldf.table_name), + ldf.SQLconnection, + ) end = time.time() - #append benchmark data to file - benchmark_data = {'executor_name':['SQL'], 'query_action':['cardinality'], 'time':[end-start], 'length':[ldf.length]} - benchmark_df = pandas.DataFrame(data = benchmark_data) - benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + # append benchmark data to file + benchmark_data = { + "executor_name": ["SQL"], + "query_action": ["cardinality"], + "time": [end - start], + "length": [ldf.length], + } + benchmark_df = pandas.DataFrame(data=benchmark_data) + benchmark_df.to_csv( + "C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv", + mode="a", + header=False, + index=False, + ) cardinality[attr] = list(card_query["count"])[0] ldf.cardinality = cardinality - def get_unique_values(self, ldf:LuxDataFrame): + def get_unique_values(self, ldf: LuxDataFrame): unique_vals = {} for attr in list(ldf.columns): start = time.time() - unique_query = pandas.read_sql("SELECT Distinct({}) FROM {}".format(attr, ldf.table_name), ldf.SQLconnection) + unique_query = pandas.read_sql( + "SELECT Distinct({}) FROM {}".format(attr, ldf.table_name), + ldf.SQLconnection, + ) end = time.time() - #append benchmark data to file - benchmark_data = {'executor_name':['SQL'], 'query_action':['unique_values'], 'time':[end-start], 'length':[ldf.length]} - benchmark_df = pandas.DataFrame(data = benchmark_data) - benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + # append benchmark data to file + benchmark_data = { + "executor_name": ["SQL"], + "query_action": ["unique_values"], + "time": [end - start], + "length": [ldf.length], + } + benchmark_df = pandas.DataFrame(data=benchmark_data) + benchmark_df.to_csv( + "C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv", + mode="a", + header=False, + index=False, + ) unique_vals[attr] = list(unique_query[attr]) ldf.unique_values = unique_vals - def compute_data_type(self, ldf:LuxDataFrame): + def compute_data_type(self, ldf: LuxDataFrame): data_type_lookup = {} sql_dtypes = {} self.get_cardinality(ldf) if "." in ldf.table_name: - table_name = ldf.table_name[ldf.table_name.index(".")+1:] + table_name = ldf.table_name[ldf.table_name.index(".") + 1 :] else: table_name = ldf.table_name - #get the data types of the attributes in the SQL table + # get the data types of the attributes in the SQL table for attr in list(ldf.columns): start = time.time() - datatype_query = "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{}' AND COLUMN_NAME = '{}'".format(table_name, attr) - datatype = list(pandas.read_sql(datatype_query, ldf.SQLconnection)['data_type'])[0] + datatype_query = "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{}' AND COLUMN_NAME = '{}'".format( + table_name, attr + ) + datatype = list( + pandas.read_sql(datatype_query, ldf.SQLconnection)["data_type"] + )[0] end = time.time() - #append benchmark data to file - benchmark_data = {'executor_name':['SQL'], 'query_action':['data_type'], 'time':[end-start], 'length':[ldf.length]} - benchmark_df = pandas.DataFrame(data = benchmark_data) - benchmark_df.to_csv('C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv', mode='a', header=False, index=False) + # append benchmark data to file + benchmark_data = { + "executor_name": ["SQL"], + "query_action": ["data_type"], + "time": [end - start], + "length": [ldf.length], + } + benchmark_df = pandas.DataFrame(data=benchmark_data) + benchmark_df.to_csv( + "C:/Users/thyne/Documents/GitHub/thyne-lux/sql_benchmarking.csv", + mode="a", + header=False, + index=False, + ) sql_dtypes[attr] = datatype - data_type = {"quantitative":[], "ordinal":[], "nominal":[], "temporal":[], "id":[]} + data_type = { + "quantitative": [], + "ordinal": [], + "nominal": [], + "temporal": [], + "id": [], + } for attr in list(ldf.columns): if str(attr).lower() in ["month", "year"]: data_type_lookup[attr] = "temporal" data_type["temporal"].append(attr) - elif sql_dtypes[attr] in ["character", "character varying", "boolean", "uuid", "text"]: + elif sql_dtypes[attr] in [ + "character", + "character varying", + "boolean", + "uuid", + "text", + ]: data_type_lookup[attr] = "nominal" data_type["nominal"].append(attr) - elif sql_dtypes[attr] in ["integer", "numeric", "decimal", "bigint", "real", "smallint", "smallserial", "serial"]: + elif sql_dtypes[attr] in [ + "integer", + "numeric", + "decimal", + "bigint", + "real", + "smallint", + "smallserial", + "serial", + ]: if ldf.cardinality[attr] < 13: data_type_lookup[attr] = "nominal" data_type["nominal"].append(attr) - elif check_if_id_like(ldf,attr): + elif check_if_id_like(ldf, attr): ldf.data_type_lookup[attr] = "id" else: data_type_lookup[attr] = "quantitative" @@ -466,9 +718,11 @@ def compute_data_type(self, ldf:LuxDataFrame): ldf.data_type_lookup = data_type_lookup ldf.data_type = data_type - def compute_data_model(self, ldf:LuxDataFrame): + def compute_data_model(self, ldf: LuxDataFrame): ldf.data_model = { "measure": ldf.data_type["quantitative"], - "dimension": ldf.data_type["ordinal"] + ldf.data_type["nominal"] + ldf.data_type["temporal"] + "dimension": ldf.data_type["ordinal"] + + ldf.data_type["nominal"] + + ldf.data_type["temporal"], } ldf.data_model_lookup = ldf.executor.reverseMapping(ldf.data_model) diff --git a/lux/interestingness/interestingness.py b/lux/interestingness/interestingness.py index 9d175583..75f50a34 100644 --- a/lux/interestingness/interestingness.py +++ b/lux/interestingness/interestingness.py @@ -212,8 +212,12 @@ def deviation_from_overall( int Score describing how different the vis is from the overall vis """ - v_filter_size = get_filtered_size(filter_specs, ldf) - v_size = len(vis.data) + if ldf.executor_type == "Pandas": + v_filter_size = get_filtered_size(filter_specs, ldf) + v_size = len(vis.data) + else: + v_filter_size = vis._vis_data.length + v_size = ldf.length v_filter = vis.data[msr_attribute] total = v_filter.sum() v_filter = v_filter / total # normalize by total to get ratio diff --git a/sql_benchmarking.csv b/sql_benchmarking.csv index 725d5254..20654d60 100644 --- a/sql_benchmarking.csv +++ b/sql_benchmarking.csv @@ -1,342 +1,15378 @@ executor_name,query_action,time,length -SQL,data_length,0.8699960708618164, +SQL,get_attributes,0.1270003318786621, +SQL,cardinality,0.0030269622802734375, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0009703636169433594, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0009925365447998047, +SQL,cardinality,0.0009703636169433594, +SQL,cardinality,0.0009930133819580078, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.001979827880859375, +SQL,data_type,0.00400996208190918, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0030274391174316406, +SQL,data_type,0.0029714107513427734, +SQL,data_type,0.0029811859130859375, +SQL,data_type,0.002959728240966797, +SQL,data_type,0.0029723644256591797, +SQL,data_type,0.004034280776977539, +SQL,data_type,0.00397181510925293, +SQL,data_type,0.003999471664428711, +SQL,data_length,0.0019752979278564453, +SQL,unique_values,0.0020301342010498047,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010285377502441406,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0010287761688232422,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010242462158203125,392 +SQL,min_max,0.0009746551513671875,392 +SQL,get_attributes,0.12503695487976074, +SQL,cardinality,0.003004312515258789, +SQL,cardinality,0.0020246505737304688, +SQL,cardinality,0.0009620189666748047, +SQL,cardinality,0.0009629726409912109, +SQL,cardinality,0.0009784698486328125, +SQL,cardinality,0.0010242462158203125, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009603500366210938, +SQL,data_type,0.00396275520324707, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003986835479736328, +SQL,data_type,0.0029916763305664062, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.003000497817993164, +SQL,data_length,0.0, +SQL,unique_values,0.0020215511322021484,392 +SQL,unique_values,0.0009577274322509766,392 +SQL,unique_values,0.00095367431640625,392 +SQL,unique_values,0.0009572505950927734,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0020093917846679688,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010178089141845703,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0009710788726806641,392 +SQL,min_max,0.001001596450805664,392 +SQL,get_attributes,0.12799739837646484, +SQL,cardinality,0.002997875213623047, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0020258426666259766, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009968280792236328, +SQL,cardinality,0.0009734630584716797, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009968280792236328, +SQL,cardinality,0.0010175704956054688, +SQL,data_type,0.004024982452392578, +SQL,data_type,0.003997087478637695, +SQL,data_type,0.003984928131103516, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0029973983764648438, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.002969980239868164, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003970146179199219, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0009877681732177734,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0019752979278564453,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0009729862213134766,392 +SQL,min_max,0.000997304916381836,392 +SQL,min_max,0.0009770393371582031,392 +SQL,get_attributes,0.12499618530273438, +SQL,cardinality,0.0030117034912109375, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.002002239227294922, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.0030188560485839844, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.006000518798828125, +SQL,data_type,0.0030007362365722656, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010104179382324219,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.002010822296142578,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020093917846679688,392 +SQL,min_max,0.0009999275207519531,392 +SQL,get_attributes,0.13800048828125, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.003990888595581055, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004007816314697266, +SQL,data_type,0.006002664566040039, +SQL,data_type,0.0030045509338378906, +SQL,data_type,0.00702357292175293, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.000995635986328125,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010085105895996094,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,get_attributes,0.130997896194458, +SQL,cardinality,0.003999948501586914, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010051727294921875, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0020003318786621094, +SQL,data_type,0.004029035568237305, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.0039730072021484375, +SQL,data_type,0.003970623016357422, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0030291080474853516, +SQL,data_type,0.00302886962890625, +SQL,data_type,0.00302886962890625, +SQL,data_type,0.00403141975402832, +SQL,data_length,0.0009737014770507812, +SQL,unique_values,0.0009903907775878906,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.001010894775390625,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001020193099975586,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,min_max,0.0010302066802978516,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010304450988769531,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.0020296573638916016,392 +SQL,get_attributes,0.20099949836730957, +SQL,cardinality,0.005998849868774414, +SQL,cardinality,0.0019969940185546875, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0019948482513427734, +SQL,cardinality,0.001996755599975586, +SQL,data_type,0.006999492645263672, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.005997180938720703, +SQL,data_type,0.0059812068939208984, +SQL,data_type,0.006000041961669922, +SQL,data_type,0.00600433349609375, +SQL,data_type,0.0039937496185302734, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.005003213882446289, +SQL,data_type,0.004996538162231445, +SQL,data_length,0.0019979476928710938, +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.003004789352416992,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.001996755599975586,392 +SQL,unique_values,0.001997232437133789,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.001999378204345703,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.002002239227294922,392 +SQL,get_attributes,0.12900018692016602, +SQL,cardinality,0.004011869430541992, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.00103759765625, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0019664764404296875, +SQL,cardinality,0.0019745826721191406, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.003996133804321289, +SQL,data_type,0.0030364990234375, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.0039958953857421875, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.0029630661010742188, +SQL,data_type,0.002988576889038086, +SQL,data_length,0.0010237693786621094, +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001039266586303711,392 +SQL,unique_values,0.0009770393371582031,392 +SQL,unique_values,0.0010128021240234375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009882450103759766,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009667873382568359,392 +SQL,min_max,0.0009622573852539062,392 +SQL,min_max,0.0009701251983642578,392 +SQL,min_max,0.001064300537109375,392 +SQL,get_attributes,0.12800002098083496, +SQL,cardinality,0.0039975643157958984, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0020024776458740234, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009989738464355469, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003002166748046875, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.004004001617431641, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000570297241211,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0019986629486083984,392 +SQL,get_attributes,0.13600420951843262, +SQL,cardinality,0.00299835205078125, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0020186901092529297, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.001010894775390625, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010063648223876953, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001001119613647461, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.006000518798828125, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.0030012130737304688, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.00101470947265625,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009794235229492188,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0009851455688476562,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0009968280792236328,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.001001119613647461,392 +SQL,get_attributes,0.13199782371520996, +SQL,cardinality,0.004000425338745117, +SQL,cardinality,0.002016782760620117, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0019991397857666016, +SQL,data_type,0.004013776779174805, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.004013776779174805, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.0049970149993896484, +SQL,data_type,0.0040056705474853516, +SQL,data_type,0.004998922348022461, +SQL,data_type,0.0050029754638671875, +SQL,data_length,0.001001596450805664, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002010345458984375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0019958019256591797,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010042190551757812,392 +SQL,min_max,0.001997709274291992,392 +SQL,min_max,0.0020067691802978516,392 +SQL,min_max,0.0010144710540771484,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.0010037422180175781,392 +SQL,scatter,0.010002851486206055,392 +SQL,scatter,0.005995512008666992,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.005998849868774414,392 +SQL,scatter,0.007001399993896484,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.00500178337097168,392 +SQL,scatter,0.005002737045288086,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.004998207092285156,392 +SQL,scatter,0.004998207092285156,392 +SQL,scatter,0.006003141403198242,392 +SQL,scatter,0.004997968673706055,392 +SQL,scatter,0.006000041961669922,392 +SQL,scatter,0.008002519607543945,392 +SQL,scatter,0.004997730255126953,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.005001068115234375,392 +SQL,bar/line,0.007001161575317383,392 +SQL,bar/line,0.006995201110839844,392 +SQL,bar/line,0.007001638412475586,392 +SQL,bar/line,0.006003856658935547,392 +SQL,get_attributes,0.13495707511901855, +SQL,cardinality,0.0029969215393066406, +SQL,cardinality,0.002004384994506836, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009694099426269531, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.002997159957885742, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020041465759277344,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.0010006427764892578,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.003999471664428711,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.004998922348022461,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.005008220672607422,392 +SQL,scatter,0.006001949310302734,392 +SQL,scatter,0.005001544952392578,392 +SQL,scatter,0.004001617431640625,392 +SQL,scatter,0.0039980411529541016,392 +SQL,scatter,0.003998994827270508,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.004002809524536133,392 +SQL,scatter,0.003998756408691406,392 +SQL,scatter,0.004998207092285156,392 +SQL,scatter,0.004002094268798828,392 +SQL,bar/line,0.007999897003173828,392 +SQL,bar/line,0.005997180938720703,392 +SQL,bar/line,0.006002187728881836,392 +SQL,get_attributes,0.022998571395874023, +SQL,cardinality,0.0030024051666259766, +SQL,cardinality,0.0019872188568115234, +SQL,cardinality,0.0009915828704833984, +SQL,cardinality,0.0009932518005371094, +SQL,cardinality,0.0019927024841308594, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.00101470947265625, +SQL,cardinality,0.0009915828704833984, +SQL,cardinality,0.0019927024841308594, +SQL,data_type,0.004007577896118164, +SQL,data_type,0.004010200500488281, +SQL,data_type,0.005008697509765625, +SQL,data_type,0.00498509407043457, +SQL,data_type,0.004010438919067383, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003988981246948242, +SQL,data_type,0.004004716873168945, +SQL,data_length,0.000993490219116211, +SQL,unique_values,0.0020067691802978516,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,unique_values,0.0010089874267578125,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0009953975677490234,392 +SQL,unique_values,0.0010113716125488281,392 +SQL,min_max,0.001009225845336914,392 +SQL,min_max,0.001996278762817383,392 +SQL,min_max,0.0009911060333251953,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0019998550415039062,392 +SQL,scatter,0.0060100555419921875,392 +SQL,scatter,0.0060122013092041016,392 +SQL,scatter,0.0059871673583984375,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.004995822906494141,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.005009174346923828,392 +SQL,scatter,0.004001140594482422,392 +SQL,scatter,0.00600123405456543,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.003998279571533203,392 +SQL,scatter,0.00400233268737793,392 +SQL,scatter,0.0029985904693603516,392 +SQL,scatter,0.0029976367950439453,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.003002166748046875,392 +SQL,scatter,0.0029985904693603516,392 +SQL,scatter,0.004002809524536133,392 +SQL,scatter,0.0039975643157958984,392 +SQL,scatter,0.005002498626708984,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.006000518798828125,392 +SQL,binning,0.0040013790130615234,392 +SQL,binning,0.030997276306152344,392 +SQL,scatter,0.005001068115234375,392 +SQL,get_attributes,0.02299666404724121, +SQL,cardinality,0.0027625560760498047, +SQL,cardinality,0.0019979476928710938, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.004003286361694336, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0019893646240234375,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010137557983398438,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010073184967041016,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009999275207519531,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.004996776580810547,392 +SQL,scatter,0.0039708614349365234,392 +SQL,scatter,0.0050029754638671875,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.005028724670410156,392 +SQL,scatter,0.00497126579284668,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.0060007572174072266,392 +SQL,scatter,0.004998207092285156,392 +SQL,scatter,0.007001638412475586,392 +SQL,scatter,0.004997730255126953,392 +SQL,scatter,0.0050013065338134766,392 +SQL,scatter,0.003998994827270508,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.00400090217590332,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.004997968673706055,392 +SQL,scatter,0.004001140594482422,392 +SQL,scatter,0.005017995834350586,392 +SQL,bar/line,0.007999897003173828,392 +SQL,bar/line,0.0060002803802490234,392 +SQL,bar/line,0.005997657775878906,392 +SQL,get_attributes,0.02200007438659668, +SQL,cardinality,0.002998828887939453, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0010292530059814453, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010080337524414062, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0010318756103515625, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.0029964447021484375, +SQL,data_type,0.0040378570556640625, +SQL,data_type,0.005969047546386719, +SQL,data_type,0.0060007572174072266, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.002997875213623047, +SQL,data_length,0.0010120868682861328, +SQL,unique_values,0.0020084381103515625,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.001004934310913086,392 +SQL,min_max,0.0010068416595458984,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010013580322265625,392 +SQL,binning,0.0049991607666015625,392 +SQL,binning,0.005002498626708984,392 +SQL,binning,0.0060007572174072266,392 +SQL,get_attributes,0.021000146865844727, +SQL,cardinality,0.0029985904693603516, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0039899349212646484, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003998756408691406, +SQL,data_length,0.0009982585906982422, +SQL,unique_values,0.000997304916381836,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00101470947265625,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010001659393310547,392 +SQL,binning,0.004999876022338867,392 +SQL,binning,0.004999876022338867,392 +SQL,binning,0.007997274398803711,392 +SQL,get_attributes,0.02200031280517578, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.000986337661743164, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009808540344238281, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010018348693847656, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.0029976367950439453, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0049991607666015625, +SQL,data_type,0.003005504608154297, +SQL,data_type,0.006000995635986328, +SQL,data_type,0.0029973983764648438, +SQL,data_type,0.003000497817993164, +SQL,data_length,0.0020012855529785156, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010113716125488281,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001001119613647461,392 +SQL,binning,0.005998849868774414,392 +SQL,binning,0.003989696502685547,392 +SQL,binning,0.00599980354309082,392 +SQL,get_attributes,0.022001266479492188, +SQL,cardinality,0.0019981861114501953, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010075569152832031, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020012855529785156, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.005024909973144531, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003000020980834961, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00098419189453125,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0019893646240234375,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009968280792236328,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0030167102813720703,392 +SQL,bar/line,0.004999637603759766,392 +SQL,get_attributes,0.019997358322143555, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0020024776458740234, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010144710540771484, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004004240036010742, +SQL,data_type,0.0030298233032226562, +SQL,data_type,0.004029512405395508, +SQL,data_type,0.002971172332763672, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.001971006393432617,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.002001047134399414,392 +SQL,bar/line,0.00699925422668457,392 +SQL,get_attributes,0.024953842163085938, +SQL,cardinality,0.002996683120727539, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.001995086669921875, +SQL,cardinality,0.0009963512420654297, +SQL,cardinality,0.0010156631469726562, +SQL,cardinality,0.0010237693786621094, +SQL,cardinality,0.0020127296447753906, +SQL,cardinality,0.0009815692901611328, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003009319305419922, +SQL,data_type,0.003966569900512695, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004037380218505859, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004036426544189453, +SQL,data_type,0.003961801528930664, +SQL,data_type,0.005010843276977539, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0030088424682617188,392 +SQL,unique_values,0.0009748935699462891,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010094642639160156,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0009946823120117188,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010068416595458984,392 +SQL,min_max,0.0009629726409912109,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0020132064819335938,392 +SQL,min_max,0.0009696483612060547,392 +SQL,min_max,0.0010063648223876953,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.003998517990112305,392 +SQL,scatter,0.004000186920166016,392 +SQL,get_attributes,0.023001432418823242, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0009660720825195312, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010080337524414062, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010371208190917969, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.00403594970703125, +SQL,data_type,0.005036354064941406, +SQL,data_type,0.003965616226196289, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003010272979736328, +SQL,data_type,0.002974987030029297, +SQL,data_type,0.003966093063354492, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.001035928726196289,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010364055633544922,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010356903076171875,392 +SQL,unique_values,0.0019826889038085938,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0010066032409667969,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0020024776458740234,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020008087158203125,392 +SQL,binning,0.003000020980834961,392 +SQL,binning,0.0030014514923095703,392 +SQL,get_attributes,0.020996570587158203, +SQL,cardinality,0.0029993057250976562, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010056495666503906, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.004996538162231445, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.003002166748046875, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.002993345260620117, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0029990673065185547, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.001986980438232422,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010170936584472656,392 +SQL,unique_values,0.00099945068359375,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0020012855529785156,392 +SQL,bar/line,0.004998445510864258,392 +SQL,bar/line,0.005001544952392578,392 +SQL,bar/line,0.004998922348022461,392 +SQL,get_attributes,0.020998716354370117, +SQL,cardinality,0.0029993057250976562, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009946823120117188, +SQL,cardinality,0.0009856224060058594, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010221004486083984, +SQL,cardinality,0.002000093460083008, +SQL,data_type,0.005000591278076172, +SQL,data_type,0.005011796951293945, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.005001068115234375, +SQL,data_type,0.004003286361694336, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010199546813964844,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0019958019256591797,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.0010068416595458984,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.0009942054748535156,392 +SQL,min_max,0.0020051002502441406,392 +SQL,min_max,0.0020012855529785156,392 +SQL,bar/line,0.011997699737548828,392 +SQL,get_attributes,0.020998001098632812, +SQL,cardinality,0.0030045509338378906, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009996891021728516, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.0029964447021484375, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004007577896118164, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010123252868652344,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.002002239227294922,392 +SQL,min_max,0.001999378204345703,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020036697387695312,392 +SQL,min_max,0.0010008811950683594,392 +SQL,scatter,0.007999181747436523,392 +SQL,scatter,0.00698399543762207,392 +SQL,get_attributes,0.020998716354370117, +SQL,cardinality,0.0030062198638916016, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0019974708557128906, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001003265380859375, +SQL,cardinality,0.0010042190551757812, +SQL,cardinality,0.0, +SQL,cardinality,0.0009984970092773438, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.005002260208129883, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001016378402709961,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.000990152359008789,392 +SQL,min_max,0.0010006427764892578,392 +SQL,scatter,0.00400090217590332,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.004015207290649414,392 +SQL,scatter,0.004998683929443359,392 +SQL,scatter,0.003984928131103516,392 +SQL,scatter,0.0050008296966552734,392 +SQL,get_attributes,0.02197098731994629, +SQL,cardinality,0.0029993057250976562, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010027885437011719, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003972291946411133, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0030274391174316406, +SQL,data_type,0.00302886962890625, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.0009992122650146484, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.000990152359008789,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.000995635986328125,392 +SQL,min_max,0.0010004043579101562,392 +SQL,scatter,0.005002260208129883,392 +SQL,get_attributes,0.023962974548339844, +SQL,cardinality,0.002998828887939453, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001013040542602539, +SQL,cardinality,0.0010061264038085938, +SQL,cardinality,0.0020029544830322266, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.001997709274291992, +SQL,cardinality,0.0020017623901367188, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004988431930541992, +SQL,data_type,0.0059969425201416016, +SQL,data_type,0.005001544952392578, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.004001140594482422, +SQL,data_length,0.0010018348693847656, +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009868144989013672,392 +SQL,bar/line,0.028000593185424805,392 +SQL,bar/line,0.004999876022338867,392 +SQL,bar/line,0.005001544952392578,392 +SQL,get_attributes,0.01999974250793457, +SQL,cardinality,0.003000020980834961, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0030007362365722656, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009891986846923828,392 +SQL,unique_values,0.0010073184967041016,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010051727294921875,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.001998424530029297,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010001659393310547,392 +SQL,bar/line,0.004999637603759766,392 +SQL,bar/line,0.004998683929443359,392 +SQL,bar/line,0.006000518798828125,392 +SQL,get_attributes,0.025995492935180664, +SQL,cardinality,0.003000497817993164, +SQL,cardinality,0.0009806156158447266, +SQL,cardinality,0.001974344253540039, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010366439819335938, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.002969980239868164, +SQL,data_type,0.00397038459777832, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0030035972595214844, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003000020980834961, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.001010894775390625,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010170936584472656,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0020017623901367188,392 +SQL,bar/line,0.012998819351196289,392 +SQL,get_attributes,0.02099466323852539, +SQL,cardinality,0.002999544143676758, +SQL,cardinality,0.0020029544830322266, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0020093917846679688, +SQL,cardinality,0.0010094642639160156, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.005003213882446289, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0040035247802734375, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.004013776779174805, +SQL,data_type,0.0029828548431396484, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.005999326705932617, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009884834289550781,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0019931793212890625,392 +SQL,bar/line,0.012992143630981445,392 +SQL,get_attributes,0.020998477935791016, +SQL,cardinality,0.0030002593994140625, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0009965896606445312, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.003011465072631836, +SQL,data_length,0.0009980201721191406, +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010085105895996094,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0010187625885009766,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,min_max,0.0009949207305908203,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.002000093460083008,392 +SQL,bar/line,0.004999637603759766,392 +SQL,get_attributes,0.020996570587158203, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0019969940185546875, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0010035037994384766, +SQL,cardinality,0.0009996891021728516, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.005002737045288086, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.0029897689819335938, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.005002260208129883, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004001617431640625, +SQL,data_length,0.0009813308715820312, +SQL,unique_values,0.000988006591796875,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002020597457885742,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0020096302032470703,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0019998550415039062,392 +SQL,binning,0.0060002803802490234,392 +SQL,get_attributes,0.021000146865844727, +SQL,cardinality,0.0029935836791992188, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010020732879638672, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.004014253616333008, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.0050013065338134766, +SQL,data_type,0.0060007572174072266, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004996061325073242, +SQL,data_length,0.0019981861114501953, +SQL,unique_values,0.001016378402709961,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009922981262207031,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0020020008087158203,392 +SQL,binning,0.0039975643157958984,392 +SQL,get_attributes,0.020998001098632812, +SQL,cardinality,0.003998279571533203, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.002007722854614258, +SQL,cardinality,0.0010182857513427734, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0019974708557128906, +SQL,cardinality,0.0010035037994384766, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.0039825439453125, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.002996683120727539, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.005003213882446289, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.0009992122650146484, +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0019974708557128906,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020041465759277344,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0019989013671875,392 +SQL,bar/line,0.008003711700439453,392 +SQL,get_attributes,0.024996519088745117, +SQL,cardinality,0.0019974708557128906, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0020182132720947266, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010020732879638672, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0029976367950439453, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.0029993057250976562, +SQL,data_length,0.0010023117065429688, +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.002000570297241211,392 +SQL,min_max,0.0020139217376708984,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0019996166229248047,392 +SQL,bar/line,0.009008407592773438,392 +SQL,get_attributes,0.02099895477294922, +SQL,cardinality,0.002999544143676758, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.002003192901611328, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.002994060516357422, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.0029935836791992188, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.004011631011962891, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0019936561584472656,392 +SQL,unique_values,0.0010118484497070312,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010044574737548828,392 +SQL,unique_values,0.001995563507080078,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.0010080337524414062,392 +SQL,min_max,0.002006053924560547,392 +SQL,bar/line,0.005002021789550781,392 +SQL,scatter,0.0050008296966552734,392 +SQL,bar/line,0.005001544952392578,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.003000020980834961,392 +SQL,get_attributes,0.13303780555725098, +SQL,cardinality,0.0029878616333007812, +SQL,cardinality,0.002022981643676758, +SQL,cardinality,0.0009810924530029297, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010254383087158203, +SQL,cardinality,0.0010383129119873047, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0040416717529296875, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.004002094268798828, +SQL,data_length,0.0010018348693847656, +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.0010097026824951172,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009911060333251953,392 +SQL,unique_values,0.0010292530059814453,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009932518005371094,392 +SQL,unique_values,0.00101470947265625,392 +SQL,unique_values,0.0010094642639160156,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0020008087158203125,392 +SQL,data_length,0.0010006427764892578,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0019960403442382812,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0020036697387695312,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020003318786621094,392 +SQL,get_attributes,0.005002021789550781, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0010135173797607422, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0020003318786621094, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003988981246948242, +SQL,data_type,0.00400233268737793, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0030014514923095703,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009946823120117188,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010001659393310547,392 +SQL,get_attributes,0.025978803634643555, +SQL,cardinality,0.0029916763305664062, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.002021312713623047, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.004998683929443359, +SQL,data_type,0.005002737045288086, +SQL,data_length,0.0020029544830322266, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.000997781753540039,392 +SQL,min_max,0.0010080337524414062,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0009996891021728516,392 +SQL,data_length,0.0009996891021728516,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010066032409667969,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009961128234863281,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010018348693847656,392 +SQL,get_attributes,0.005001544952392578, +SQL,cardinality,0.0019979476928710938, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.000997781753540039, +SQL,data_type,0.003997087478637695, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.005995273590087891, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.0039975643157958984, +SQL,data_length,0.0009989738464355469, +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010082721710205078,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.002001523971557617,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.002004384994506836,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.0010120868682861328,392 +SQL,min_max,0.0009992122650146484,392 +SQL,scatter,0.003996849060058594,392 +SQL,get_attributes,0.01999974250793457, +SQL,cardinality,0.003000497817993164, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.000988006591796875, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.0029828548431396484, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003997802734375, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003000020980834961, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0009822845458984375,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002001523971557617,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0020008087158203125,392 +SQL,data_length,0.0,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009937286376953125,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.000997781753540039,392 +SQL,min_max,0.000997304916381836,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.002003192901611328,392 +SQL,get_attributes,0.005001068115234375, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010142326354980469, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.0039958953857421875, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0029976367950439453, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.005000114440917969, +SQL,data_length,0.0010020732879638672, +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009999275207519531,392 +SQL,get_attributes,0.02399420738220215, +SQL,cardinality,0.001958608627319336, +SQL,cardinality,0.0010380744934082031, +SQL,cardinality,0.001047372817993164, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010101795196533203, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.004006862640380859, +SQL,data_type,0.0029675960540771484, +SQL,data_type,0.003984928131103516, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0029976367950439453, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.002999544143676758, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0020024776458740234,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0020003318786621094,392 +SQL,data_length,0.0009989738464355469,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.001996755599975586,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009963512420654297,392 +SQL,min_max,0.0010056495666503906,392 +SQL,get_attributes,0.003997325897216797, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0019845962524414062, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010046958923339844, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010037422180175781, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009992122650146484, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.002996206283569336, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0029985904693603516, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.003368377685546875,392 +SQL,binning,0.005997419357299805,392 +SQL,binning,0.004999399185180664,392 +SQL,binning,0.004996061325073242,392 +SQL,get_attributes,0.02299809455871582, +SQL,cardinality,0.003002643585205078, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0020062923431396484, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.002001047134399414, +SQL,data_type,0.0029947757720947266, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.003997802734375, +SQL,data_type,0.004003286361694336, +SQL,data_type,0.0049974918365478516, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0030031204223632812, +SQL,data_length,0.0009815692901611328, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0010082721710205078,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009980201721191406,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010204315185546875,392 +SQL,data_length,0.0009965896606445312,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.000993490219116211,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009999275207519531,392 +SQL,get_attributes,0.004000425338745117, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.00099945068359375, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0029969215393066406, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.002997159957885742, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009844303131103516,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010080337524414062,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0009942054748535156,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.002004861831665039,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009999275207519531,392 +SQL,binning,0.004000186920166016,392 +SQL,binning,0.00400090217590332,392 +SQL,binning,0.0049991607666015625,392 SQL,get_attributes,0.022000789642333984, -SQL,cardinality,0.23900198936462402, -SQL,cardinality,0.8590314388275146, -SQL,cardinality,0.24300098419189453, -SQL,cardinality,0.24300074577331543, -SQL,cardinality,0.8979983329772949, -SQL,cardinality,1.4349722862243652, -SQL,cardinality,1.4909999370574951, -SQL,cardinality,0.2719850540161133, -SQL,cardinality,0.2609994411468506, -SQL,cardinality,0.2989978790283203, -SQL,cardinality,0.3519899845123291, +SQL,cardinality,0.0030002593994140625, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010149478912353516, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.002003192901611328, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0020058155059814453, +SQL,cardinality,0.0010018348693847656, +SQL,data_type,0.0030059814453125, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.004002809524536133, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0010137557983398438,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010006427764892578,392 +SQL,data_length,0.0010006427764892578,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.0020024776458740234,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009908676147460938,392 +SQL,min_max,0.0009996891021728516,392 +SQL,get_attributes,0.005002260208129883, +SQL,cardinality,0.0009942054748535156, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0009951591491699219, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.005002737045288086, +SQL,data_type,0.006003856658935547, +SQL,data_type,0.004998922348022461, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.0030117034912109375, +SQL,data_type,0.0060007572174072266, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010063648223876953,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0009796619415283203,392 +SQL,min_max,0.002000570297241211,392 +SQL,binning,0.004998207092285156,392 +SQL,binning,0.003999233245849609,392 +SQL,binning,0.00599980354309082,392 +SQL,get_attributes,0.022994279861450195, +SQL,cardinality,0.0029997825622558594, +SQL,cardinality,0.0019702911376953125, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009624958038330078, +SQL,cardinality,0.0009648799896240234, +SQL,cardinality,0.0020313262939453125, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.00298309326171875, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.00400233268737793, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009894371032714844,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.002007722854614258,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.00099945068359375,392 +SQL,data_length,0.0010001659393310547,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.000985860824584961,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0020012855529785156,392 +SQL,get_attributes,0.005002021789550781, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0020029544830322266, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009996891021728516, SQL,data_type,0.004001140594482422, +SQL,data_type,0.002981901168823242, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.005040168762207031, +SQL,data_type,0.003997802734375, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.001005411148071289,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009925365447998047,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010018348693847656,392 +SQL,bar/line,0.00499415397644043,392 +SQL,get_attributes,0.020998716354370117, +SQL,cardinality,0.001998424530029297, +SQL,cardinality,0.0009963512420654297, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003002166748046875, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.004014015197753906, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.0050029754638671875, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.00301361083984375, +SQL,data_type,0.00400233268737793, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.000980377197265625,392 +SQL,unique_values,0.001996278762817383,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010111331939697266,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0010042190551757812,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010085105895996094,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009915828704833984,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010004043579101562,392 +SQL,get_attributes,0.0050013065338134766, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0010082721710205078, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009882450103759766, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0020024776458740234, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.006001472473144531, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.002998828887939453, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0009989738464355469,392 +SQL,bar/line,0.00799870491027832,392 +SQL,get_attributes,0.02397012710571289, +SQL,cardinality,0.0030012130737304688, +SQL,cardinality,0.0010287761688232422, +SQL,cardinality,0.001032114028930664, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010101795196533203, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009646415710449219, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003030061721801758, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0029897689819335938, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0030040740966796875, +SQL,data_type,0.002998828887939453, +SQL,data_length,0.0, +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0020029544830322266,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,data_length,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010135173797607422,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020034313201904297,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.002001523971557617,392 +SQL,get_attributes,0.0039997100830078125, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010156631469726562, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.002000570297241211, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.0040073394775390625, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.005998849868774414, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.005004405975341797, +SQL,data_length,0.0019979476928710938, +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0019965171813964844,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.0009953975677490234,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009980201721191406,392 +SQL,min_max,0.0009968280792236328,392 +SQL,min_max,0.0020036697387695312,392 +SQL,scatter,0.0069980621337890625,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.004990100860595703,392 +SQL,get_attributes,0.023999691009521484, +SQL,cardinality,0.00500035285949707, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0020020008087158203, +SQL,data_type,0.003996849060058594, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.00599980354309082, +SQL,data_type,0.0039975643157958984, +SQL,data_type,0.0049970149993896484, +SQL,data_type,0.004999637603759766, +SQL,data_type,0.00600123405456543, +SQL,data_length,0.0, +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0020194053649902344,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0010116100311279297,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0009975433349609375,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009984970092773438,392 +SQL,data_length,0.0010018348693847656,392 +SQL,unique_values,0.0020046234130859375,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0009958744049072266,392 +SQL,get_attributes,0.005001068115234375, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.00199127197265625, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010137557983398438, +SQL,cardinality,0.001997709274291992, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.00400853157043457, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004002809524536133, +SQL,data_length,0.0010030269622802734, +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010013580322265625,392 +SQL,binning,0.0029990673065185547,392 +SQL,binning,0.003000020980834961,392 +SQL,get_attributes,0.02099776268005371, +SQL,cardinality,0.0029993057250976562, +SQL,cardinality,0.0010042190551757812, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.001003265380859375, +SQL,cardinality,0.0009851455688476562, +SQL,cardinality,0.0020029544830322266, +SQL,cardinality,0.002003192901611328, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0030012130737304688, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010170936584472656,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0009982585906982422,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009984970092773438,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002016782760620117,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0020024776458740234,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,get_attributes,0.004997730255126953, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009975433349609375, +SQL,cardinality,0.002004861831665039, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001016378402709961, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.00399470329284668, +SQL,data_type,0.005011081695556641, +SQL,data_type,0.004981517791748047, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.0030007362365722656, +SQL,data_length,0.0010156631469726562, +SQL,unique_values,0.0010104179382324219,392 +SQL,unique_values,0.000985860824584961,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010232925415039062,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,bar/line,0.0049991607666015625,392 +SQL,bar/line,0.005001068115234375,392 +SQL,bar/line,0.0050008296966552734,392 +SQL,get_attributes,0.022995948791503906, +SQL,cardinality,0.003009319305419922, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0020051002502441406, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0009832382202148438, +SQL,cardinality,0.0010037422180175781, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.005001068115234375, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0029823780059814453, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.002982616424560547, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0029976367950439453, +SQL,data_length,0.0009813308715820312, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010161399841308594,392 +SQL,min_max,0.0009996891021728516,392 +SQL,data_length,0.0019979476928710938,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.001004934310913086,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0010023117065429688,392 +SQL,get_attributes,0.003999948501586914, +SQL,cardinality,0.0020062923431396484, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009965896606445312, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004004240036010742, +SQL,data_type,0.005001068115234375, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.0029969215393066406, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001004934310913086,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009849071502685547,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0009951591491699219,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0020008087158203125,392 +SQL,scatter,0.004998207092285156,392 +SQL,get_attributes,0.0260317325592041, +SQL,cardinality,0.0039691925048828125, +SQL,cardinality,0.0019969940185546875, +SQL,cardinality,0.00102996826171875, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009865760803222656, +SQL,cardinality,0.0010058879852294922, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.0040280818939208984, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.002972841262817383, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.003027200698852539, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.001028299331665039,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010077953338623047,392 +SQL,min_max,0.000997781753540039,392 +SQL,min_max,0.0010268688201904297,392 +SQL,min_max,0.0009732246398925781,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009732246398925781,392 +SQL,data_length,0.0009925365447998047,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001007080078125,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010004043579101562,392 +SQL,get_attributes,0.005002021789550781, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0009965896606445312, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.004010915756225586, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004015922546386719, +SQL,data_type,0.005003452301025391, +SQL,data_type,0.00600743293762207, +SQL,data_type,0.0039691925048828125, +SQL,data_length,0.001995086669921875, +SQL,unique_values,0.0019714832305908203,392 +SQL,unique_values,0.0009908676147460938,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.0020051002502441406,392 +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0010077953338623047,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0010035037994384766,392 +SQL,min_max,0.0009906291961669922,392 +SQL,scatter,0.006997346878051758,392 +SQL,scatter,0.004998445510864258,392 +SQL,get_attributes,0.019003629684448242, +SQL,cardinality,0.003000497817993164, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.002000570297241211, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0030024051666259766, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.003998994827270508, +SQL,data_length,0.0, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.001999378204345703,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.002000570297241211,392 +SQL,data_length,0.0010006427764892578,392 +SQL,unique_values,0.002016305923461914,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0020186901092529297,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009877681732177734,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009942054748535156,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009968280792236328,392 +SQL,get_attributes,0.004000663757324219, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001003265380859375, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010061264038085938, +SQL,cardinality,0.0009815692901611328, +SQL,data_type,0.003989458084106445, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0050008296966552734, +SQL,data_type,0.003995418548583984, +SQL,data_type,0.0029969215393066406, +SQL,data_type,0.004001140594482422, +SQL,data_length,0.0010018348693847656, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020134449005126953,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009834766387939453,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010077953338623047,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,min_max,0.0020036697387695312,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020029544830322266,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004000663757324219,392 +SQL,get_attributes,0.019970178604125977, +SQL,cardinality,0.0029997825622558594, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010154247283935547, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.005004167556762695, +SQL,data_type,0.005995750427246094, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.0030024051666259766, +SQL,data_type,0.0060007572174072266, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.002994537353515625, +SQL,data_type,0.0030012130737304688, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010037422180175781,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010020732879638672,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.0009903907775878906,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010144710540771484,392 +SQL,get_attributes,0.004002094268798828, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009996891021728516, +SQL,data_type,0.003993034362792969, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003991127014160156, +SQL,data_type,0.004005908966064453, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0029931068420410156, +SQL,data_type,0.0029997825622558594, +SQL,data_length,0.0010101795196533203, +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.000995635986328125,392 +SQL,unique_values,0.0010073184967041016,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0010030269622802734,392 +SQL,get_attributes,0.021998167037963867, +SQL,cardinality,0.0030019283294677734, +SQL,cardinality,0.001997709274291992, +SQL,cardinality,0.0009884834289550781, +SQL,cardinality,0.001994609832763672, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.0010306835174560547, +SQL,cardinality,0.001027822494506836, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.000997304916381836, +SQL,data_type,0.003973245620727539, +SQL,data_type,0.003969669342041016, +SQL,data_type,0.004031181335449219, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.0029540061950683594, +SQL,data_type,0.00399470329284668, +SQL,data_type,0.003999471664428711, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010294914245605469,392 +SQL,unique_values,0.0010302066802978516,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009648799896240234,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010294914245605469,392 +SQL,min_max,0.000997781753540039,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.002002239227294922,392 +SQL,scatter,0.005028724670410156,392 +SQL,get_attributes,0.020971059799194336, +SQL,cardinality,0.0030269622802734375, +SQL,cardinality,0.0010263919830322266, +SQL,cardinality,0.0010068416595458984, +SQL,cardinality,0.00102996826171875, +SQL,cardinality,0.0009698867797851562, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0010120868682861328, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.001007080078125, +SQL,data_type,0.0030248165130615234, +SQL,data_type,0.003971576690673828, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0039865970611572266, +SQL,data_type,0.004004240036010742, +SQL,data_type,0.002960205078125, +SQL,data_type,0.0059719085693359375, +SQL,data_type,0.003008604049682617, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010166168212890625,392 +SQL,unique_values,0.0010271072387695312,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010292530059814453,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0010216236114501953,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.001975536346435547,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0020093917846679688,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,unique_values,0.0010068416595458984,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,min_max,0.0009970664978027344,392 +SQL,min_max,0.001970052719116211,392 +SQL,min_max,0.0020258426666259766,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.00102996826171875,392 +SQL,get_attributes,0.005000114440917969, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.000980377197265625, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0009799003601074219, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.0039708614349365234, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004034996032714844, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0029714107513427734, +SQL,data_type,0.0029709339141845703, +SQL,data_type,0.002970457077026367, +SQL,data_type,0.002997159957885742, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009663105010986328,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002032041549682617,392 +SQL,unique_values,0.0010383129119873047,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010294914245605469,392 +SQL,unique_values,0.001962423324584961,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.001020193099975586,392 +SQL,min_max,0.0020291805267333984,392 +SQL,min_max,0.0009710788726806641,392 +SQL,bar/line,0.004973888397216797,392 +SQL,bar/line,0.006028890609741211,392 +SQL,bar/line,0.0049974918365478516,392 +SQL,get_attributes,0.0230257511138916, +SQL,cardinality,0.004026651382446289, +SQL,cardinality,0.0019712448120117188, +SQL,cardinality,0.0009801387786865234, +SQL,cardinality,0.0010347366333007812, +SQL,cardinality,0.0009660720825195312, +SQL,cardinality,0.0019638538360595703, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010266304016113281, +SQL,data_type,0.003980875015258789, +SQL,data_type,0.005004167556762695, +SQL,data_type,0.004968166351318359, +SQL,data_type,0.003030538558959961, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.004027843475341797, +SQL,data_type,0.004980564117431641, +SQL,data_length,0.0009882450103759766, +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010180473327636719,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.002031087875366211,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,min_max,0.0009834766387939453,392 +SQL,min_max,0.0009961128234863281,392 +SQL,min_max,0.0009720325469970703,392 +SQL,min_max,0.0009753704071044922,392 +SQL,min_max,0.0010004043579101562,392 +SQL,data_length,0.0009911060333251953,392 +SQL,unique_values,0.0020258426666259766,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001971721649169922,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010304450988769531,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0019729137420654297,392 +SQL,min_max,0.0019712448120117188,392 +SQL,get_attributes,0.005003452301025391, +SQL,cardinality,0.0030007362365722656, +SQL,cardinality,0.0010311603546142578, +SQL,cardinality,0.0009648799896240234, +SQL,cardinality,0.0009632110595703125, +SQL,cardinality,0.0009753704071044922, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010235309600830078, +SQL,cardinality,0.0009887218475341797, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.0029871463775634766, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004028797149658203, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.0039708614349365234, +SQL,data_type,0.006972551345825195, +SQL,data_type,0.004017353057861328, +SQL,data_type,0.004027366638183594, +SQL,data_type,0.003972530364990234, +SQL,data_length,0.0010266304016113281, +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0020313262939453125,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0009942054748535156,392 +SQL,unique_values,0.0009899139404296875,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.0009970664978027344,392 +SQL,min_max,0.0010020732879638672,392 +SQL,bar/line,0.00500035285949707,392 +SQL,bar/line,0.004999399185180664,392 +SQL,bar/line,0.006027936935424805,392 +SQL,get_attributes,0.02499246597290039, +SQL,cardinality,0.0030002593994140625, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020062923431396484, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.001972198486328125, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010063648223876953, +SQL,cardinality,0.0010313987731933594, +SQL,data_type,0.003994464874267578, +SQL,data_type,0.003996133804321289, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.00299072265625, +SQL,data_type,0.0030298233032226562, +SQL,data_type,0.004004955291748047, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004002809524536133, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0009944438934326172,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0020291805267333984,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010304450988769531,392 +SQL,unique_values,0.0009639263153076172,392 +SQL,min_max,0.0010311603546142578,392 +SQL,min_max,0.0009927749633789062,392 +SQL,min_max,0.0009741783142089844,392 +SQL,min_max,0.0010259151458740234,392 +SQL,min_max,0.0010006427764892578,392 +SQL,data_length,0.00098419189453125,392 +SQL,unique_values,0.0010094642639160156,392 +SQL,unique_values,0.0009644031524658203,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0009675025939941406,392 +SQL,unique_values,0.0010457038879394531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009946823120117188,392 +SQL,min_max,0.0009851455688476562,392 +SQL,get_attributes,0.004999399185180664, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010292530059814453, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0010056495666503906, +SQL,data_type,0.004029512405395508, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003984212875366211, +SQL,data_type,0.003994464874267578, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029954910278320312, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.006999015808105469, +SQL,data_type,0.004001140594482422, +SQL,data_length,0.002000093460083008, +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019576549530029297,392 +SQL,unique_values,0.000965118408203125,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0009832382202148438,392 +SQL,min_max,0.001033782958984375,392 +SQL,min_max,0.0010266304016113281,392 +SQL,min_max,0.0019707679748535156,392 +SQL,min_max,0.0020029544830322266,392 +SQL,scatter,0.005002260208129883,392 +SQL,get_attributes,0.020997285842895508, +SQL,cardinality,0.004002571105957031, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.001027822494506836, +SQL,cardinality,0.001028299331665039, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.00096893310546875, +SQL,cardinality,0.0009813308715820312, +SQL,cardinality,0.0010251998901367188, +SQL,cardinality,0.0009706020355224609, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0029964447021484375, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003000974655151367, +SQL,data_length,0.0019724369049072266, +SQL,unique_values,0.0020062923431396484,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0009696483612060547,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010051727294921875,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009722709655761719,392 +SQL,data_length,0.002000570297241211,392 +SQL,unique_values,0.0020110607147216797,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0009953975677490234,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010268688201904297,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0010259151458740234,392 +SQL,min_max,0.0010099411010742188,392 +SQL,min_max,0.001970529556274414,392 +SQL,min_max,0.0010149478912353516,392 +SQL,get_attributes,0.005010843276977539, +SQL,cardinality,0.0020384788513183594, +SQL,cardinality,0.0010361671447753906, +SQL,cardinality,0.0009601116180419922, +SQL,cardinality,0.0009703636169433594, +SQL,cardinality,0.0010273456573486328, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009698867797851562, +SQL,cardinality,0.0009877681732177734, +SQL,data_type,0.003039121627807617, +SQL,data_type,0.004029512405395508, +SQL,data_type,0.003996849060058594, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004028797149658203, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004027605056762695, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001972198486328125,392 +SQL,unique_values,0.001004934310913086,392 +SQL,unique_values,0.0020296573638916016,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019741058349609375,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.001032114028930664,392 +SQL,unique_values,0.001008749008178711,392 +SQL,min_max,0.0010051727294921875,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0009784698486328125,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.0010004043579101562,392 +SQL,scatter,0.004971504211425781,392 +SQL,get_attributes,0.020027875900268555, +SQL,cardinality,0.0029675960540771484, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009927749633789062, +SQL,cardinality,0.0010042190551757812, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0009655952453613281, +SQL,cardinality,0.000965118408203125, +SQL,cardinality,0.0009641647338867188, +SQL,cardinality,0.001972675323486328, +SQL,data_type,0.004027843475341797, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.0029952526092529297, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003971576690673828, +SQL,data_type,0.0040111541748046875, +SQL,data_type,0.003968000411987305, +SQL,data_type,0.00399470329284668, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0009696483612060547,392 +SQL,unique_values,0.0009737014770507812,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010292530059814453,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0009744167327880859,392 +SQL,unique_values,0.0010271072387695312,392 +SQL,min_max,0.0020284652709960938,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009679794311523438,392 +SQL,min_max,0.000997781753540039,392 +SQL,data_length,0.0010027885437011719,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009870529174804688,392 +SQL,unique_values,0.001973867416381836,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010297298431396484,392 +SQL,min_max,0.0010242462158203125,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.001013040542602539,392 +SQL,min_max,0.0009779930114746094,392 +SQL,get_attributes,0.0039975643157958984, +SQL,cardinality,0.001996755599975586, +SQL,cardinality,0.001020193099975586, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0009746551513671875, +SQL,cardinality,0.0010268688201904297, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0010221004486083984, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003956317901611328, +SQL,data_type,0.003014802932739258, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.0, +SQL,unique_values,0.001971721649169922,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010287761688232422,392 +SQL,unique_values,0.001013040542602539,392 +SQL,unique_values,0.001975536346435547,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010256767272949219,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020265579223632812,392 +SQL,min_max,0.0009729862213134766,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0009703636169433594,392 +SQL,bar/line,0.004999637603759766,392 +SQL,get_attributes,0.02299952507019043, +SQL,cardinality,0.0029990673065185547, +SQL,cardinality,0.001028299331665039, +SQL,cardinality,0.0010371208190917969, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010037422180175781, +SQL,cardinality,0.0019652843475341797, +SQL,data_type,0.005023002624511719, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0040264129638671875, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003971099853515625, +SQL,data_type,0.004038810729980469, +SQL,data_type,0.003950834274291992, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0010161399841308594,392 +SQL,unique_values,0.001995563507080078,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0010302066802978516,392 +SQL,min_max,0.0019690990447998047,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010128021240234375,392 +SQL,min_max,0.000993490219116211,392 +SQL,min_max,0.000990152359008789,392 +SQL,data_length,0.0010061264038085938,392 +SQL,unique_values,0.002011537551879883,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.001004934310913086,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.001997709274291992,392 +SQL,unique_values,0.0010302066802978516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0020101070404052734,392 +SQL,min_max,0.0009365081787109375,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.002001047134399414,392 +SQL,get_attributes,0.008001089096069336, +SQL,cardinality,0.004003763198852539, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0020029544830322266, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0010046958923339844, +SQL,cardinality,0.002000093460083008, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.003997802734375, +SQL,data_type,0.003991603851318359, +SQL,data_type,0.003988742828369141, +SQL,data_type,0.005031585693359375, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003997325897216797, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003999471664428711, +SQL,data_length,0.0020017623901367188, +SQL,unique_values,0.001992464065551758,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002003908157348633,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0010123252868652344,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009884834289550781,392 +SQL,min_max,0.0009922981262207031,392 +SQL,binning,0.003998756408691406,392 +SQL,get_attributes,0.022001266479492188, +SQL,cardinality,0.003996610641479492, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0009806156158447266, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.002020120620727539, +SQL,cardinality,0.0009975433349609375, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.0069887638092041016, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004996776580810547, +SQL,data_type,0.004998207092285156, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.003999471664428711, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.002016782760620117,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010008811950683594,392 +SQL,data_length,0.0010008811950683594,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009944438934326172,392 +SQL,min_max,0.0010004043579101562,392 +SQL,get_attributes,0.003997325897216797, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010097026824951172, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0019998550415039062, +SQL,data_type,0.0060040950775146484, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0040051937103271484, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004011631011962891, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0029990673065185547,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0019960403442382812,392 +SQL,unique_values,0.001997232437133789,392 +SQL,unique_values,0.0019974708557128906,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0010020732879638672,392 +SQL,binning,0.003000020980834961,392 +SQL,get_attributes,0.020999431610107422, +SQL,cardinality,0.003000497817993164, +SQL,cardinality,0.0010297298431396484, +SQL,cardinality,0.0009591579437255859, +SQL,cardinality,0.0009696483612060547, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0010328292846679688, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004028797149658203, +SQL,data_type,0.0030024051666259766, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0030117034912109375, +SQL,data_type,0.0030050277709960938, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003024578094482422, +SQL,data_type,0.003026723861694336, +SQL,data_length,0.0010271072387695312, +SQL,unique_values,0.0009682178497314453,392 +SQL,unique_values,0.0019707679748535156,392 +SQL,unique_values,0.0009918212890625,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0009715557098388672,392 +SQL,min_max,0.0010013580322265625,392 +SQL,data_length,0.0,392 +SQL,unique_values,0.0010313987731933594,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0010309219360351562,392 +SQL,unique_values,0.0010190010070800781,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.001971006393432617,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010066032409667969,392 +SQL,get_attributes,0.004004716873168945, +SQL,cardinality,0.002042055130004883, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0, +SQL,cardinality,0.0010294914245605469, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009603500366210938, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.001024484634399414, +SQL,data_type,0.003974199295043945, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0039975643157958984, +SQL,data_type,0.0039806365966796875, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003973245620727539, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.004005908966064453, +SQL,data_length,0.0, +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0010297298431396484,392 +SQL,unique_values,0.0010306835174560547,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020034313201904297,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0010104179382324219,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0009717941284179688,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010004043579101562,392 +SQL,bar/line,0.005997180938720703,392 +SQL,get_attributes,0.02194976806640625, +SQL,cardinality,0.003000020980834961, +SQL,cardinality,0.001004934310913086, +SQL,cardinality,0.0009963512420654297, +SQL,cardinality,0.0009739398956298828, +SQL,cardinality,0.0019800662994384766, +SQL,cardinality,0.0010273456573486328, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.000985860824584961, +SQL,cardinality,0.0009648799896240234, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.005034446716308594, +SQL,data_type,0.00397944450378418, +SQL,data_type,0.004028797149658203, +SQL,data_type,0.0029706954956054688, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004007816314697266, +SQL,data_length,0.002001047134399414, +SQL,unique_values,0.0009665489196777344,392 +SQL,unique_values,0.002033710479736328,392 +SQL,unique_values,0.000982046127319336,392 +SQL,unique_values,0.000997304916381836,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.00098419189453125,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0010268688201904297,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.001999378204345703,392 +SQL,min_max,0.0009741783142089844,392 +SQL,min_max,0.0010111331939697266,392 +SQL,data_length,0.0010008811950683594,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0019724369049072266,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.000965118408203125,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0020236968994140625,392 +SQL,unique_values,0.001024484634399414,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,min_max,0.0010194778442382812,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0010197162628173828,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0009920597076416016,392 +SQL,get_attributes,0.005029916763305664, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.001981019973754883, +SQL,cardinality,0.000972747802734375, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004998683929443359, +SQL,data_type,0.003970146179199219, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0029969215393066406, +SQL,data_type,0.005030155181884766, +SQL,data_length,0.001003265380859375, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009891986846923828,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.001031637191772461,392 +SQL,unique_values,0.0010294914245605469,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0009961128234863281,392 +SQL,min_max,0.0009851455688476562,392 +SQL,min_max,0.0009737014770507812,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009722709655761719,392 +SQL,bar/line,0.009000062942504883,392 +SQL,get_attributes,0.02102828025817871, +SQL,cardinality,0.0029757022857666016, +SQL,cardinality,0.0010247230529785156, +SQL,cardinality,0.001973390579223633, +SQL,cardinality,0.0009744167327880859, +SQL,cardinality,0.0009768009185791016, +SQL,cardinality,0.0010104179382324219, +SQL,cardinality,0.0009682178497314453, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009756088256835938, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.006972789764404297, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.0039708614349365234, +SQL,data_type,0.0030150413513183594, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.005001068115234375, +SQL,data_type,0.0029969215393066406, +SQL,data_type,0.004001617431640625, +SQL,data_length,0.0010304450988769531, +SQL,unique_values,0.0020208358764648438,392 +SQL,unique_values,0.001997709274291992,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0010242462158203125,392 +SQL,unique_values,0.000965118408203125,392 +SQL,unique_values,0.0009667873382568359,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0009860992431640625,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0009715557098388672,392 +SQL,min_max,0.001973390579223633,392 +SQL,min_max,0.0019903182983398438,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.001001119613647461,392 +SQL,data_length,0.0019714832305908203,392 +SQL,unique_values,0.0009741783142089844,392 +SQL,unique_values,0.001975536346435547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009620189666748047,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0009615421295166016,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009808540344238281,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.0010347366333007812,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009701251983642578,392 +SQL,get_attributes,0.003996610641479492, +SQL,cardinality,0.0020284652709960938, +SQL,cardinality,0.0010325908660888672, +SQL,cardinality,0.0009632110595703125, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0009620189666748047, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.001962900161743164, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0009729862213134766, +SQL,data_type,0.0029709339141845703, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0049970149993896484, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.003996133804321289, +SQL,data_type,0.00397038459777832, +SQL,data_type,0.006009578704833984, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.001020669937133789,392 +SQL,unique_values,0.0009667873382568359,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009684562683105469,392 +SQL,min_max,0.0009717941284179688,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0009746551513671875,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0020258426666259766,392 +SQL,scatter,0.004029512405395508,392 +SQL,bar/line,0.005972623825073242,392 +SQL,scatter,0.003997325897216797,392 +SQL,scatter,0.0050008296966552734,392 +SQL,bar/line,0.005000114440917969,392 +SQL,scatter,0.003993988037109375,392 +SQL,get_attributes,0.12403631210327148, +SQL,cardinality,0.002973794937133789, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0020318031311035156, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0010340213775634766, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010271072387695312, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.003973722457885742, +SQL,data_type,0.004024505615234375, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004025697708129883, +SQL,data_type,0.005026578903198242, +SQL,data_type,0.003974199295043945, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0009815692901611328,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0020258426666259766,392 +SQL,min_max,0.0010066032409667969,392 +SQL,min_max,0.0009744167327880859,392 +SQL,min_max,0.00201416015625,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0020008087158203125,392 +SQL,data_length,0.0010044574737548828,392 +SQL,unique_values,0.0010268688201904297,392 +SQL,unique_values,0.0010271072387695312,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010256767272949219,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010313987731933594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009741783142089844,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0009810924530029297,392 +SQL,get_attributes,0.004998922348022461, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.001039743423461914, +SQL,cardinality,0.001001596450805664, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029871463775634766, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0030088424682617188, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.0039730072021484375, +SQL,data_type,0.0040209293365478516, +SQL,data_length,0.002004384994506836, +SQL,unique_values,0.001974344253540039,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010275840759277344,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0019898414611816406,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,min_max,0.0009744167327880859,392 +SQL,min_max,0.0019745826721191406,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0009682178497314453,392 +SQL,min_max,0.0010292530059814453,392 +SQL,scatter,0.003970623016357422,392 +SQL,get_attributes,0.1379554271697998, +SQL,cardinality,0.004000425338745117, +SQL,cardinality,0.002032041549682617, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010156631469726562, +SQL,cardinality,0.0019707679748535156, +SQL,cardinality,0.0010251998901367188, +SQL,cardinality,0.0009751319885253906, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.00402522087097168, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0030274391174316406, +SQL,data_type,0.004027605056762695, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003002166748046875, +SQL,data_type,0.004976511001586914, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0019736289978027344,392 +SQL,unique_values,0.0009913444519042969,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0010292530059814453,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.001009225845336914,392 +SQL,min_max,0.0009739398956298828,392 +SQL,min_max,0.00096893310546875,392 +SQL,data_length,0.0009686946868896484,392 +SQL,unique_values,0.0009672641754150391,392 +SQL,unique_values,0.0009765625,392 +SQL,unique_values,0.0010309219360351562,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010290145874023438,392 +SQL,unique_values,0.0009741783142089844,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009677410125732422,392 +SQL,unique_values,0.002001523971557617,392 +SQL,min_max,0.0009691715240478516,392 +SQL,min_max,0.0009951591491699219,392 +SQL,min_max,0.001004934310913086,392 +SQL,min_max,0.0019724369049072266,392 +SQL,min_max,0.0010037422180175781,392 +SQL,get_attributes,0.004999399185180664, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0009834766387939453, +SQL,cardinality,0.0010263919830322266, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.000972747802734375, +SQL,cardinality,0.0009942054748535156, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0010218620300292969, +SQL,data_type,0.00397038459777832, +SQL,data_type,0.003970623016357422, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0029959678649902344, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.004029989242553711, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.0030236244201660156, +SQL,data_type,0.003000020980834961, +SQL,data_length,0.0010013580322265625, +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009765625,392 +SQL,unique_values,0.0010256767272949219,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,unique_values,0.0019741058349609375,392 +SQL,unique_values,0.002025604248046875,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.00096893310546875,392 +SQL,min_max,0.0009944438934326172,392 +SQL,min_max,0.0010037422180175781,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0010347366333007812,392 +SQL,min_max,0.0010380744934082031,392 +SQL,get_attributes,0.021999120712280273, +SQL,cardinality,0.002998828887939453, +SQL,cardinality,0.0010259151458740234, +SQL,cardinality,0.002012014389038086, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010368824005126953, +SQL,cardinality,0.0010116100311279297, +SQL,cardinality,0.0009682178497314453, +SQL,cardinality,0.0010292530059814453, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0030286312103271484, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.003972768783569336, +SQL,data_length,0.0009961128234863281, +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0010302066802978516,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0020284652709960938,392 +SQL,unique_values,0.0010297298431396484,392 +SQL,unique_values,0.0009686946868896484,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010349750518798828,392 +SQL,unique_values,0.0009768009185791016,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0010297298431396484,392 +SQL,min_max,0.0010349750518798828,392 +SQL,data_length,0.001970529556274414,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0009694099426269531,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009887218475341797,392 +SQL,unique_values,0.002027273178100586,392 +SQL,unique_values,0.0010263919830322266,392 +SQL,unique_values,0.0009746551513671875,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,min_max,0.0009756088256835938,392 +SQL,min_max,0.0009512901306152344,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0010290145874023438,392 +SQL,min_max,0.0009720325469970703,392 +SQL,get_attributes,0.004993915557861328, +SQL,cardinality,0.002002716064453125, +SQL,cardinality,0.001973867416381836, +SQL,cardinality,0.0009763240814208984, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010085105895996094, +SQL,cardinality,0.0010361671447753906, +SQL,cardinality,0.0019690990447998047, +SQL,cardinality,0.0009641647338867188, +SQL,cardinality,0.001967191696166992, +SQL,data_type,0.006006002426147461, +SQL,data_type,0.005004167556762695, +SQL,data_type,0.003996133804321289, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004003286361694336, +SQL,data_type,0.0030050277709960938, +SQL,data_type,0.004014253616333008, +SQL,data_length,0.0009965896606445312, +SQL,unique_values,0.0019965171813964844,392 +SQL,unique_values,0.0020110607147216797,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.002250194549560547,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.001995086669921875,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.002022266387939453,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010106563568115234,392 +SQL,scatter,0.004998445510864258,392 +SQL,get_attributes,0.024035930633544922, +SQL,cardinality,0.0030295848846435547, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.001026153564453125, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009784698486328125, +SQL,cardinality,0.0009744167327880859, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009961128234863281, +SQL,data_type,0.004030704498291016, +SQL,data_type,0.0030024051666259766, +SQL,data_type,0.003975391387939453, +SQL,data_type,0.0040476322174072266, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003033876419067383, +SQL,data_type,0.003003835678100586, +SQL,data_type,0.003973245620727539, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.001001596450805664, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010242462158203125,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010292530059814453,392 +SQL,unique_values,0.001032114028930664,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0010170936584472656,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,min_max,0.0009732246398925781,392 +SQL,min_max,0.0009729862213134766,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0009732246398925781,392 +SQL,data_length,0.0010030269622802734,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.001996278762817383,392 +SQL,unique_values,0.0009737014770507812,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010080337524414062,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0009670257568359375,392 +SQL,unique_values,0.0009639263153076172,392 +SQL,unique_values,0.000972747802734375,392 +SQL,min_max,0.0009851455688476562,392 +SQL,min_max,0.0010044574737548828,392 +SQL,min_max,0.000993967056274414,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010008811950683594,392 +SQL,get_attributes,0.004029750823974609, +SQL,cardinality,0.0019724369049072266, +SQL,cardinality,0.0010304450988769531, +SQL,cardinality,0.0009737014770507812, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.0009741783142089844, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010251998901367188, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003017425537109375, +SQL,data_type,0.006028652191162109, +SQL,data_type,0.004010438919067383, +SQL,data_type,0.003988027572631836, +SQL,data_type,0.004995584487915039, +SQL,data_type,0.004004478454589844, +SQL,data_type,0.003998756408691406, +SQL,data_length,0.002000093460083008, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010311603546142578,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020155906677246094,392 +SQL,min_max,0.0010275840759277344,392 +SQL,min_max,0.0009720325469970703,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0020296573638916016,392 +SQL,min_max,0.0009808540344238281,392 +SQL,get_attributes,0.0210268497467041, +SQL,cardinality,0.0029990673065185547, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.000972747802734375, +SQL,cardinality,0.0009741783142089844, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0010306835174560547, +SQL,cardinality,0.0009744167327880859, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.003995418548583984, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.002960681915283203, +SQL,data_type,0.003962278366088867, +SQL,data_type,0.0029594898223876953, +SQL,data_type,0.0029752254486083984, +SQL,data_type,0.004010677337646484, +SQL,data_type,0.003962993621826172, +SQL,data_type,0.0039615631103515625, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010275840759277344,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010304450988769531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009744167327880859,392 +SQL,min_max,0.001031637191772461,392 +SQL,min_max,0.0020279884338378906,392 +SQL,min_max,0.0009732246398925781,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010292530059814453,392 +SQL,data_length,0.0009741783142089844,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010178089141845703,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001032114028930664,392 +SQL,unique_values,0.002027273178100586,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,min_max,0.0009615421295166016,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0009741783142089844,392 +SQL,min_max,0.0010266304016113281,392 +SQL,min_max,0.001997709274291992,392 +SQL,get_attributes,0.0039615631103515625, +SQL,cardinality,0.0029973983764648438, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0020143985748291016, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010268688201904297, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.001031637191772461, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0029969215393066406, +SQL,data_type,0.006021261215209961, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.003978252410888672, +SQL,data_type,0.005999088287353516, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003973960876464844, +SQL,data_type,0.004027605056762695, +SQL,data_length,0.0009989738464355469, +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010361671447753906,392 +SQL,unique_values,0.0009748935699462891,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.002003192901611328,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010421276092529297,392 +SQL,binning,0.00596165657043457,392 +SQL,binning,0.00400233268737793,392 +SQL,binning,0.005988121032714844,392 +SQL,get_attributes,0.02302694320678711, +SQL,cardinality,0.002997875213623047, +SQL,cardinality,0.002031087875366211, +SQL,cardinality,0.0009737014770507812, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.00095367431640625, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.002015829086303711, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.005006551742553711, +SQL,data_type,0.003028392791748047, +SQL,data_type,0.003967761993408203, +SQL,data_type,0.003973960876464844, +SQL,data_type,0.004031658172607422, +SQL,data_type,0.0029876232147216797, +SQL,data_type,0.00399470329284668, +SQL,data_length,0.0010175704956054688, +SQL,unique_values,0.0020279884338378906,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.0009763240814208984,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009784698486328125,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0020036697387695312,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.002027750015258789,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010018348693847656,392 +SQL,data_length,0.0010006427764892578,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009748935699462891,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010271072387695312,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009670257568359375,392 +SQL,unique_values,0.0,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010285377502441406,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020008087158203125,392 +SQL,get_attributes,0.004974365234375, +SQL,cardinality,0.0010256767272949219, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.000993490219116211, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009751319885253906, +SQL,cardinality,0.0010287761688232422, +SQL,cardinality,0.0009748935699462891, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003991127014160156, +SQL,data_type,0.003991365432739258, +SQL,data_type,0.003973722457885742, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003998994827270508, +SQL,data_length,0.0009784698486328125, +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001007080078125,392 +SQL,unique_values,0.0009915828704833984,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,min_max,0.0010035037994384766,392 +SQL,min_max,0.0020363330841064453,392 +SQL,min_max,0.0019605159759521484,392 +SQL,min_max,0.0020186901092529297,392 +SQL,min_max,0.001976490020751953,392 +SQL,binning,0.004998683929443359,392 +SQL,binning,0.004002809524536133,392 +SQL,binning,0.004993438720703125,392 +SQL,get_attributes,0.023000240325927734, +SQL,cardinality,0.003003358840942383, +SQL,cardinality,0.0020279884338378906, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0019974708557128906, +SQL,cardinality,0.0010037422180175781, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0019884109497070312, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009732246398925781, +SQL,data_type,0.004999399185180664, +SQL,data_type,0.00600123405456543, +SQL,data_type,0.004030466079711914, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003029346466064453, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.004029512405395508, +SQL,data_type,0.003998279571533203, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.002023458480834961,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010137557983398438,392 +SQL,unique_values,0.0009946823120117188,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0009953975677490234,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00096893310546875,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.0010061264038085938,392 +SQL,data_length,0.0,392 +SQL,unique_values,0.001026153564453125,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009908676147460938,392 +SQL,unique_values,0.0020258426666259766,392 +SQL,unique_values,0.0009744167327880859,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002029895782470703,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.000982046127319336,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0009660720825195312,392 +SQL,min_max,0.0010063648223876953,392 +SQL,min_max,0.001024484634399414,392 +SQL,get_attributes,0.0039997100830078125, +SQL,cardinality,0.002974987030029297, +SQL,cardinality,0.0010266304016113281, +SQL,cardinality,0.000972747802734375, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.002032041549682617, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009784698486328125, +SQL,cardinality,0.000997781753540039, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.00402522087097168, +SQL,data_type,0.003962039947509766, +SQL,data_type,0.003030538558959961, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0040285587310791016, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.0010266304016113281, +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009624958038330078,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010271072387695312,392 +SQL,unique_values,0.0009746551513671875,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009746551513671875,392 +SQL,min_max,0.0010097026824951172,392 +SQL,min_max,0.0009884834289550781,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009992122650146484,392 +SQL,binning,0.0060291290283203125,392 +SQL,binning,0.004024028778076172,392 +SQL,binning,0.006027936935424805,392 +SQL,get_attributes,0.02300238609313965, +SQL,cardinality,0.0030002593994140625, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001031637191772461, +SQL,cardinality,0.001961946487426758, +SQL,cardinality,0.0009739398956298828, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.001999378204345703, +SQL,data_type,0.005976438522338867, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003961324691772461, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.0040264129638671875, +SQL,data_type,0.003026723861694336, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003973484039306641, +SQL,data_length,0.0010209083557128906, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010385513305664062,392 +SQL,unique_values,0.002039194107055664,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010378360748291016,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0020062923431396484,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009996891021728516,392 +SQL,data_length,0.0009644031524658203,392 +SQL,unique_values,0.0009765625,392 +SQL,unique_values,0.0010194778442382812,392 +SQL,unique_values,0.0010235309600830078,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.001004934310913086,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0009741783142089844,392 +SQL,get_attributes,0.004041433334350586, +SQL,cardinality,0.001983642578125, +SQL,cardinality,0.0010058879852294922, +SQL,cardinality,0.0009744167327880859, +SQL,cardinality,0.0009946823120117188, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010368824005126953, +SQL,cardinality,0.0009758472442626953, +SQL,cardinality,0.0009984970092773438, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.002951383590698242, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.004027605056762695, +SQL,data_type,0.0030367374420166016, +SQL,data_type,0.006035327911376953, +SQL,data_type,0.003971576690673828, +SQL,data_type,0.003975868225097656, +SQL,data_type,0.003999233245849609, +SQL,data_length,0.0, +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010371208190917969,392 +SQL,unique_values,0.0009694099426269531,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010106563568115234,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0010378360748291016,392 +SQL,min_max,0.0010385513305664062,392 +SQL,min_max,0.0010364055633544922,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010144710540771484,392 +SQL,bar/line,0.0060007572174072266,392 +SQL,get_attributes,0.022023677825927734, +SQL,cardinality,0.0020258426666259766, +SQL,cardinality,0.0019884109497070312, +SQL,cardinality,0.0020067691802978516, +SQL,cardinality,0.001979827880859375, +SQL,cardinality,0.0010328292846679688, +SQL,cardinality,0.0009729862213134766, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0019626617431640625, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004965066909790039, +SQL,data_type,0.004036903381347656, +SQL,data_type,0.003964424133300781, +SQL,data_type,0.0030367374420166016, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004996776580810547, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.003000020980834961, +SQL,data_length,0.0009953975677490234, +SQL,unique_values,0.0010156631469726562,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001009225845336914,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.000993967056274414,392 +SQL,unique_values,0.0010135173797607422,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010063648223876953,392 +SQL,min_max,0.0009682178497314453,392 +SQL,min_max,0.0010423660278320312,392 +SQL,min_max,0.000990152359008789,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009937286376953125,392 +SQL,data_length,0.0009984970092773438,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.0020220279693603516,392 +SQL,unique_values,0.0020232200622558594,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.001996278762817383,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.002001523971557617,392 +SQL,min_max,0.0010082721710205078,392 +SQL,min_max,0.001026153564453125,392 +SQL,min_max,0.0009951591491699219,392 +SQL,min_max,0.0010342597961425781,392 +SQL,min_max,0.0020008087158203125,392 +SQL,get_attributes,0.005036830902099609, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0009665489196777344, +SQL,cardinality,0.0009753704071044922, +SQL,cardinality,0.00197601318359375, +SQL,cardinality,0.0009975433349609375, +SQL,cardinality,0.0010437965393066406, +SQL,cardinality,0.0010275840759277344, +SQL,cardinality,0.0010271072387695312, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0050280094146728516, +SQL,data_type,0.004009723663330078, +SQL,data_type,0.002986431121826172, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.0009961128234863281, +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009751319885253906,392 +SQL,unique_values,0.0009667873382568359,392 +SQL,unique_values,0.0010356903076171875,392 +SQL,unique_values,0.0009763240814208984,392 +SQL,unique_values,0.0009744167327880859,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.00203704833984375,392 +SQL,min_max,0.0009562969207763672,392 +SQL,min_max,0.0010530948638916016,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0009970664978027344,392 +SQL,min_max,0.0020253658294677734,392 +SQL,bar/line,0.007975578308105469,392 +SQL,get_attributes,0.02397298812866211, +SQL,cardinality,0.0029916763305664062, +SQL,cardinality,0.001998424530029297, +SQL,cardinality,0.0009737014770507812, +SQL,cardinality,0.0019712448120117188, +SQL,cardinality,0.0010304450988769531, +SQL,cardinality,0.0010311603546142578, +SQL,cardinality,0.0009744167327880859, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001972198486328125, +SQL,data_type,0.004035472869873047, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0040280818939208984, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003969430923461914, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0029985904693603516, +SQL,data_length,0.0010225772857666016, +SQL,unique_values,0.001027822494506836,392 +SQL,unique_values,0.00102996826171875,392 +SQL,unique_values,0.0009744167327880859,392 +SQL,unique_values,0.0010266304016113281,392 +SQL,unique_values,0.0010242462158203125,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010304450988769531,392 +SQL,unique_values,0.0009686946868896484,392 +SQL,unique_values,0.0009677410125732422,392 +SQL,min_max,0.0009970664978027344,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0019757747650146484,392 +SQL,min_max,0.001026153564453125,392 +SQL,data_length,0.0010273456573486328,392 +SQL,unique_values,0.0009658336639404297,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0009958744049072266,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010297298431396484,392 +SQL,unique_values,0.0010128021240234375,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009813308715820312,392 +SQL,min_max,0.0009908676147460938,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0019986629486083984,392 +SQL,min_max,0.0010302066802978516,392 +SQL,min_max,0.001968860626220703,392 +SQL,get_attributes,0.005000114440917969, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0010027885437011719, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0009737014770507812, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0020170211791992188, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003962516784667969, +SQL,data_type,0.002950906753540039, +SQL,data_type,0.004042148590087891, +SQL,data_type,0.003954648971557617, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.00401759147644043, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.001997232437133789,392 +SQL,unique_values,0.0010135173797607422,392 +SQL,unique_values,0.0010280609130859375,392 +SQL,unique_values,0.0009779930114746094,392 +SQL,unique_values,0.0019905567169189453,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009722709655761719,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,min_max,0.00102996826171875,392 +SQL,min_max,0.0010275840759277344,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010323524475097656,392 +SQL,min_max,0.0009810924530029297,392 +SQL,scatter,0.0050280094146728516,392 +SQL,scatter,0.006020784378051758,392 +SQL,scatter,0.00400090217590332,392 +SQL,get_attributes,0.023967981338500977, +SQL,cardinality,0.0020258426666259766, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0009741783142089844, +SQL,cardinality,0.0010313987731933594, +SQL,cardinality,0.0009751319885253906, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.0040624141693115234, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.005022525787353516, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0005102157592773438,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.001016378402709961,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0010323524475097656,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0020072460174560547,392 +SQL,min_max,0.0009636878967285156,392 +SQL,min_max,0.0020198822021484375,392 +SQL,data_length,0.0010001659393310547,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010213851928710938,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010085105895996094,392 +SQL,unique_values,0.0010356903076171875,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010273456573486328,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.001995563507080078,392 +SQL,min_max,0.0020003318786621094,392 +SQL,get_attributes,0.00598454475402832, +SQL,cardinality,0.003002166748046875, +SQL,cardinality,0.0019981861114501953, +SQL,cardinality,0.0020036697387695312, +SQL,cardinality,0.001016855239868164, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009987354278564453, +SQL,data_type,0.004998445510864258, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003986835479736328, +SQL,data_type,0.00700068473815918, +SQL,data_type,0.004821300506591797, +SQL,data_type,0.005001068115234375, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.003998994827270508, +SQL,data_length,0.0009992122650146484, +SQL,unique_values,0.0020263195037841797,392 +SQL,unique_values,0.0020041465759277344,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.00101470947265625,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0019974708557128906,392 +SQL,min_max,0.001977205276489258,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.000995635986328125,392 +SQL,min_max,0.0010132789611816406,392 +SQL,binning,0.0030007362365722656,392 +SQL,binning,0.004006624221801758,392 +SQL,get_attributes,0.020997047424316406, +SQL,cardinality,0.003001689910888672, +SQL,cardinality,0.001004934310913086, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0009970664978027344, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.001996278762817383, +SQL,cardinality,0.0010051727294921875, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.0029964447021484375, +SQL,data_type,0.003994464874267578, +SQL,data_type,0.0030069351196289062, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0039975643157958984, +SQL,data_type,0.002996683120727539, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.002996206283569336, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.002003192901611328, +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,min_max,0.0010035037994384766,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001003265380859375,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.001997709274291992,392 +SQL,data_length,0.00099945068359375,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0019943714141845703,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0029954910278320312,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0030059814453125,392 +SQL,unique_values,0.0020051002502441406,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010170936584472656,392 +SQL,min_max,0.0010006427764892578,392 +SQL,get_attributes,0.004996776580810547, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.000995635986328125, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.001997709274291992, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010020732879638672, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0030024051666259766, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.0039975643157958984, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.0009984970092773438, +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020041465759277344,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0020041465759277344,392 +SQL,min_max,0.0010001659393310547,392 +SQL,bar/line,0.0050008296966552734,392 +SQL,bar/line,0.004999399185180664,392 +SQL,bar/line,0.003998994827270508,392 +SQL,get_attributes,0.020999431610107422, +SQL,cardinality,0.0030002593994140625, +SQL,cardinality,0.0010232925415039062, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010290145874023438, +SQL,cardinality,0.0009768009185791016, +SQL,cardinality,0.0009796619415283203, +SQL,cardinality,0.0009961128234863281, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009701251983642578, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003972053527832031, +SQL,data_type,0.0029697418212890625, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.0030040740966796875, +SQL,data_type,0.0040073394775390625, +SQL,data_type,0.003974199295043945, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004026651382446289, +SQL,data_length,0.0010268688201904297, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009746551513671875,392 +SQL,unique_values,0.0010166168212890625,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009944438934326172,392 +SQL,min_max,0.0010361671447753906,392 +SQL,min_max,0.0009958744049072266,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.000997781753540039,392 +SQL,min_max,0.0009996891021728516,392 +SQL,data_length,0.0010020732879638672,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009813308715820312,392 +SQL,unique_values,0.0009660720825195312,392 +SQL,unique_values,0.0009667873382568359,392 +SQL,unique_values,0.0010166168212890625,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001024484634399414,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.002025604248046875,392 +SQL,get_attributes,0.0040056705474853516, +SQL,cardinality,0.0009691715240478516, +SQL,cardinality,0.0009744167327880859, +SQL,cardinality,0.0010268688201904297, +SQL,cardinality,0.0009746551513671875, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.00096893310546875, +SQL,cardinality,0.002027273178100586, +SQL,data_type,0.004027843475341797, +SQL,data_type,0.003972053527832031, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.006001949310302734, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.003983736038208008, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0030155181884765625, +SQL,data_length,0.0, +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0019719600677490234,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0020253658294677734,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,unique_values,0.0010204315185546875,392 +SQL,unique_values,0.0019681453704833984,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009963512420654297,392 +SQL,min_max,0.0009732246398925781,392 +SQL,scatter,0.003999471664428711,392 +SQL,get_attributes,0.022998571395874023, +SQL,cardinality,0.003000020980834961, +SQL,cardinality,0.0010082721710205078, +SQL,cardinality,0.0009744167327880859, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0020294189453125, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009813308715820312, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001972198486328125, +SQL,data_type,0.0029871463775634766, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.004007816314697266, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.004025936126708984, +SQL,data_type,0.003003358840942383, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0030031204223632812, +SQL,data_length,0.0009629726409912109, +SQL,unique_values,0.0019919872283935547,392 +SQL,unique_values,0.0020036697387695312,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,unique_values,0.0010428428649902344,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009756088256835938,392 +SQL,unique_values,0.0009353160858154297,392 +SQL,min_max,0.002992391586303711,392 +SQL,min_max,0.001998424530029297,392 +SQL,min_max,0.001997709274291992,392 +SQL,min_max,0.0009949207305908203,392 +SQL,min_max,0.001005411148071289,392 +SQL,data_length,0.0010335445404052734,392 +SQL,unique_values,0.002003908157348633,392 +SQL,unique_values,0.0010297298431396484,392 +SQL,unique_values,0.0009737014770507812,392 +SQL,unique_values,0.0019960403442382812,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.000997304916381836,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,min_max,0.0010302066802978516,392 +SQL,min_max,0.0009763240814208984,392 +SQL,min_max,0.0020208358764648438,392 +SQL,min_max,0.0009768009185791016,392 +SQL,min_max,0.0010013580322265625,392 +SQL,get_attributes,0.0050008296966552734, +SQL,cardinality,0.0020608901977539062, +SQL,cardinality,0.0, +SQL,cardinality,0.0009739398956298828, +SQL,cardinality,0.0009670257568359375, +SQL,cardinality,0.001974344253540039, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001007080078125, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0039789676666259766, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0039751529693603516, +SQL,data_type,0.0030257701873779297, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0040285587310791016, +SQL,data_type,0.003973245620727539, +SQL,data_length,0.0009725093841552734, +SQL,unique_values,0.002995014190673828,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.001970529556274414,392 +SQL,unique_values,0.0009846687316894531,392 +SQL,min_max,0.0009629726409912109,392 +SQL,min_max,0.0010292530059814453,392 +SQL,min_max,0.0009975433349609375,392 +SQL,min_max,0.0009982585906982422,392 +SQL,min_max,0.0010004043579101562,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.004999637603759766,392 +SQL,get_attributes,0.02302861213684082, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.00096893310546875, +SQL,cardinality,0.0009849071502685547, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009734630584716797, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001970529556274414, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010294914245605469, +SQL,data_type,0.0040302276611328125, +SQL,data_type,0.004024505615234375, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004999399185180664, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.00397491455078125, +SQL,data_type,0.003998756408691406, +SQL,data_length,0.0010068416595458984, +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010066032409667969,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0010232925415039062,392 +SQL,min_max,0.0019974708557128906,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009717941284179688,392 +SQL,min_max,0.0009739398956298828,392 +SQL,min_max,0.0009953975677490234,392 +SQL,data_length,0.0009734630584716797,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0020062923431396484,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0009756088256835938,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0009970664978027344,392 +SQL,min_max,0.0009684562683105469,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0010061264038085938,392 +SQL,get_attributes,0.004996538162231445, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0009751319885253906, +SQL,cardinality,0.0009908676147460938, +SQL,cardinality,0.002035856246948242, +SQL,cardinality,0.0009968280792236328, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010175704956054688, +SQL,cardinality,0.0010342597961425781, +SQL,data_type,0.004032135009765625, +SQL,data_type,0.003969907760620117, +SQL,data_type,0.003989458084106445, +SQL,data_type,0.0030329227447509766, +SQL,data_type,0.0039675235748291016, +SQL,data_type,0.004997968673706055, +SQL,data_type,0.006027698516845703, +SQL,data_type,0.0059964656829833984, +SQL,data_type,0.004034757614135742, +SQL,data_length,0.0009989738464355469, +SQL,unique_values,0.0010263919830322266,392 +SQL,unique_values,0.0010347366333007812,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0009834766387939453,392 +SQL,unique_values,0.0009746551513671875,392 +SQL,unique_values,0.001974344253540039,392 +SQL,unique_values,0.0010099411010742188,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,min_max,0.0010297298431396484,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0009677410125732422,392 +SQL,min_max,0.0010044574737548828,392 +SQL,scatter,0.006000995635986328,392 +SQL,scatter,0.005974292755126953,392 +SQL,scatter,0.004017829895019531,392 +SQL,scatter,0.004998683929443359,392 +SQL,get_attributes,0.021028518676757812, +SQL,cardinality,0.0030007362365722656, +SQL,cardinality,0.0010306835174560547, +SQL,cardinality,0.0010313987731933594, +SQL,cardinality,0.0009753704071044922, +SQL,cardinality,0.0010266304016113281, +SQL,cardinality,0.0009739398956298828, +SQL,cardinality,0.0010268688201904297, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0020134449005126953, +SQL,data_type,0.0030264854431152344, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.004003047943115234, +SQL,data_type,0.0040051937103271484, +SQL,data_type,0.004968404769897461, +SQL,data_type,0.004026174545288086, +SQL,data_type,0.004004478454589844, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004026651382446289, +SQL,data_length,0.0009827613830566406, +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009748935699462891,392 +SQL,unique_values,0.0009660720825195312,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0019936561584472656,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0010242462158203125,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.0019762516021728516,392 +SQL,min_max,0.002024412155151367,392 +SQL,min_max,0.0010006427764892578,392 +SQL,data_length,0.0009725093841552734,392 +SQL,unique_values,0.0010080337524414062,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0009832382202148438,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,min_max,0.0009737014770507812,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.001971721649169922,392 +SQL,min_max,0.00099945068359375,392 +SQL,get_attributes,0.0040302276611328125, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0009739398956298828, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010154247283935547, +SQL,cardinality,0.0010287761688232422, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.0010230541229248047, +SQL,cardinality,0.001009225845336914, +SQL,cardinality,0.0010018348693847656, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004003286361694336, +SQL,data_type,0.007002830505371094, +SQL,data_type,0.004023551940917969, +SQL,data_type,0.0029723644256591797, +SQL,data_type,0.0029730796813964844, +SQL,data_type,0.005975008010864258, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.004995584487915039, +SQL,data_length,0.0010192394256591797, +SQL,unique_values,0.0029993057250976562,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001004934310913086,392 +SQL,unique_values,0.001004934310913086,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0010042190551757812,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010073184967041016,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0020029544830322266,392 +SQL,get_attributes,0.1439964771270752, +SQL,cardinality,0.0040013790130615234, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020024776458740234, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0020017623901367188, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.006993770599365234, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004999876022338867, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.002001523971557617,392 +SQL,data_length,0.0010204315185546875,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.001997709274291992,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.001003265380859375,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010144710540771484,392 +SQL,get_attributes,0.004999876022338867, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0009975433349609375, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.000995635986328125, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.005002260208129883, +SQL,data_type,0.0039997100830078125, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.00099945068359375,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009975433349609375,392 +SQL,min_max,0.0010094642639160156,392 +SQL,min_max,0.0010006427764892578,392 +SQL,get_attributes,0.021971464157104492, +SQL,cardinality,0.0029973983764648438, +SQL,cardinality,0.001971721649169922, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010058879852294922, +SQL,cardinality,0.0010051727294921875, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.0040130615234375, +SQL,data_type,0.004029989242553711, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.003027200698852539, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.005000591278076172, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029659271240234375, +SQL,data_type,0.003998756408691406, +SQL,data_length,0.0009989738464355469, +SQL,unique_values,0.0009739398956298828,392 +SQL,unique_values,0.0019750595092773438,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010292530059814453,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010082721710205078,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010066032409667969,392 +SQL,min_max,0.0009753704071044922,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0020012855529785156,392 +SQL,data_length,0.0010149478912353516,392 +SQL,unique_values,0.00102996826171875,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009799003601074219,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.00098419189453125,392 +SQL,min_max,0.0,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0020134449005126953,392 +SQL,get_attributes,0.005000114440917969, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010039806365966797, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0020101070404052734, +SQL,cardinality,0.001001119613647461, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.004006147384643555, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.0040013790130615234, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002008199691772461,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0020303726196289062,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009922981262207031,392 +SQL,unique_values,0.00099945068359375,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.002032756805419922,392 +SQL,min_max,0.000972747802734375,392 +SQL,scatter,0.003999233245849609,392 +SQL,get_attributes,0.02398371696472168, +SQL,cardinality,0.003000497817993164, +SQL,cardinality,0.0019659996032714844, +SQL,cardinality,0.002037525177001953, +SQL,cardinality,0.0009791851043701172, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0020008087158203125, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003996849060058594, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004015922546386719, +SQL,data_length,0.0010023117065429688, +SQL,unique_values,0.000993967056274414,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009756088256835938,392 +SQL,unique_values,0.0010116100311279297,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0009906291961669922,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.002000570297241211,392 +SQL,data_length,0.001001596450805664,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.000993490219116211,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,min_max,0.001007080078125,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.001003265380859375,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010008811950683594,392 +SQL,get_attributes,0.004003047943115234, +SQL,cardinality,0.0019981861114501953, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.003992795944213867, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.003990650177001953, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0040018558502197266, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010089874267578125,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009961128234863281,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010082721710205078,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0010073184967041016,392 +SQL,get_attributes,0.022000551223754883, +SQL,cardinality,0.0029990673065185547, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.00099945068359375, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0039997100830078125, +SQL,data_length,0.002000093460083008, +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010077953338623047,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009808540344238281,392 +SQL,unique_values,0.0010192394256591797,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0020008087158203125,392 +SQL,data_length,0.0010030269622802734,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010156631469726562,392 +SQL,unique_values,0.0010042190551757812,392 +SQL,unique_values,0.0010137557983398438,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.001003265380859375,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010161399841308594,392 +SQL,min_max,0.0010001659393310547,392 +SQL,get_attributes,0.003999948501586914, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0010056495666503906, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.00500178337097168, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.003997802734375, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003986835479736328, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004002094268798828, +SQL,data_length,0.0010013580322265625, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001013040542602539,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0010056495666503906,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010018348693847656,392 +SQL,binning,0.00600123405456543,392 +SQL,binning,0.0049953460693359375,392 +SQL,binning,0.006001472473144531,392 +SQL,get_attributes,0.02200031280517578, +SQL,cardinality,0.0030002593994140625, +SQL,cardinality,0.000995635986328125, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009987354278564453, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004994869232177734, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.00399327278137207, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0029981136322021484, +SQL,data_length,0.0010013580322265625, +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009903907775878906,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010068416595458984,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010020732879638672,392 +SQL,data_length,0.0009903907775878906,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010166168212890625,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019850730895996094,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0019989013671875,392 +SQL,get_attributes,0.003998756408691406, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009877681732177734, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0029926300048828125, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004007816314697266, +SQL,data_type,0.0030012130737304688, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010006427764892578,392 +SQL,binning,0.0049974918365478516,392 +SQL,binning,0.005003690719604492,392 +SQL,binning,0.004996776580810547,392 +SQL,get_attributes,0.021965503692626953, +SQL,cardinality,0.00298309326171875, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.001996755599975586, +SQL,cardinality,0.0019655227661132812, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0010037422180175781, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009620189666748047, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.004019737243652344, +SQL,data_type,0.003970623016357422, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003993988037109375, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0030066967010498047, +SQL,data_type,0.00397181510925293, +SQL,data_type,0.0050013065338134766, +SQL,data_length,0.000993490219116211, +SQL,unique_values,0.002028942108154297,392 +SQL,unique_values,0.0010106563568115234,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00102996826171875,392 +SQL,unique_values,0.0009670257568359375,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,min_max,0.0019698143005371094,392 +SQL,min_max,0.0010066032409667969,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.00098419189453125,392 +SQL,min_max,0.0010297298431396484,392 +SQL,data_length,0.0010290145874023438,392 +SQL,unique_values,0.0019702911376953125,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,unique_values,0.0009684562683105469,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.002010822296142578,392 +SQL,min_max,0.0030007362365722656,392 +SQL,min_max,0.001016378402709961,392 +SQL,get_attributes,0.0049991607666015625, +SQL,cardinality,0.002014636993408203, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0020067691802978516, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001001119613647461, +SQL,data_type,0.004998922348022461, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0029973983764648438, +SQL,data_type,0.0040111541748046875, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.003010988235473633, +SQL,data_type,0.005003452301025391, +SQL,data_type,0.004012584686279297, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0029997825622558594,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.00200653076171875,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.002002239227294922,392 +SQL,min_max,0.001998424530029297,392 +SQL,min_max,0.0020067691802978516,392 +SQL,binning,0.004992246627807617,392 +SQL,binning,0.007001161575317383,392 +SQL,binning,0.00599980354309082,392 +SQL,get_attributes,0.02200150489807129, +SQL,cardinality,0.0029993057250976562, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.000986337661743164, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010023117065429688, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003002643585205078, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004008293151855469, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0009980201721191406,392 +SQL,min_max,0.0019998550415039062,392 +SQL,data_length,0.0010006427764892578,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010004043579101562,392 +SQL,get_attributes,0.004000425338745117, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0020072460174560547, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.004003047943115234, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004003047943115234, +SQL,data_type,0.003988981246948242, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.004004716873168945, +SQL,data_type,0.004998922348022461, +SQL,data_type,0.003998994827270508, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020051002502441406,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.001999378204345703,392 +SQL,bar/line,0.004997968673706055,392 +SQL,get_attributes,0.018999099731445312, +SQL,cardinality,0.0030007362365722656, +SQL,cardinality,0.001989126205444336, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.004014253616333008, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.00299835205078125, +SQL,data_length,0.0010018348693847656, +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0020036697387695312,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020003318786621094,392 +SQL,data_length,0.0019991397857666016,392 +SQL,unique_values,0.0019795894622802734,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020041465759277344,392 +SQL,unique_values,0.0009925365447998047,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.002999544143676758,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009961128234863281,392 +SQL,get_attributes,0.005013942718505859, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0020024776458740234, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0060007572174072266, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.002984285354614258, +SQL,data_type,0.0029976367950439453, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0030012130737304688, +SQL,data_length,0.0, +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001018524169921875,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.002002239227294922,392 +SQL,min_max,0.0019998550415039062,392 +SQL,bar/line,0.006002187728881836,392 +SQL,get_attributes,0.03300070762634277, +SQL,cardinality,0.0029990673065185547, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.00201416015625, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0020008087158203125, +SQL,data_type,0.009978055953979492, +SQL,data_type,0.0070002079010009766, +SQL,data_type,0.006003856658935547, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.003988981246948242, +SQL,data_type,0.0039975643157958984, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.003000974655151367, +SQL,data_length,0.0, +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.0020034313201904297,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020127296447753906,392 +SQL,data_length,0.000989675521850586,392 +SQL,unique_values,0.0009922981262207031,392 +SQL,unique_values,0.0009911060333251953,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010082721710205078,392 +SQL,min_max,0.0009975433349609375,392 +SQL,min_max,0.0010085105895996094,392 +SQL,get_attributes,0.0050008296966552734, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.001997709274291992, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0009925365447998047, +SQL,cardinality,0.001005411148071289, +SQL,cardinality,0.001997709274291992, +SQL,cardinality,0.002003192901611328, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.00099945068359375, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.003006458282470703, +SQL,data_type,0.004003047943115234, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.006000518798828125, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004003047943115234, +SQL,data_type,0.0030012130737304688, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0020036697387695312,392 +SQL,unique_values,0.0009844303131103516,392 +SQL,min_max,0.0009860992431640625,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.002002239227294922,392 +SQL,min_max,0.002002239227294922,392 +SQL,scatter,0.004998683929443359,392 +SQL,scatter,0.0040013790130615234,392 +SQL,scatter,0.006999969482421875,392 +SQL,get_attributes,0.024030447006225586, +SQL,cardinality,0.0030007362365722656, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0010306835174560547, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009903907775878906, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0020067691802978516, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.005962371826171875, +SQL,data_type,0.003971576690673828, +SQL,data_type,0.004030942916870117, +SQL,data_type,0.005974292755126953, +SQL,data_type,0.004971027374267578, +SQL,data_type,0.0040166378021240234, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0039975643157958984, +SQL,data_length,0.0009710788726806641, +SQL,unique_values,0.0009694099426269531,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009961128234863281,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010178089141845703,392 +SQL,data_length,0.0010063648223876953,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010116100311279297,392 +SQL,min_max,0.002003192901611328,392 +SQL,min_max,0.0009717941284179688,392 +SQL,min_max,0.0010297298431396484,392 +SQL,get_attributes,0.0039937496185302734, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0010113716125488281, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009822845458984375, +SQL,cardinality,0.00099945068359375, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003987789154052734, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004003286361694336, +SQL,data_length,0.0010018348693847656, +SQL,unique_values,0.0009894371032714844,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.00200653076171875,392 +SQL,binning,0.0050313472747802734,392 +SQL,binning,0.0030281543731689453,392 +SQL,get_attributes,0.02399468421936035, +SQL,cardinality,0.003001689910888672, +SQL,cardinality,0.001995086669921875, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0020008087158203125, +SQL,data_type,0.003997802734375, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0030291080474853516, +SQL,data_type,0.00397181510925293, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0040035247802734375, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009961128234863281,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010046958923339844,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0020055770874023438,392 +SQL,min_max,0.0019989013671875,392 +SQL,data_length,0.0009903907775878906,392 +SQL,unique_values,0.0010097026824951172,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.001007080078125,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002000570297241211,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020024776458740234,392 +SQL,min_max,0.001989126205444336,392 +SQL,min_max,0.0019996166229248047,392 +SQL,get_attributes,0.007005214691162109, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009975433349609375, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010025501251220703, +SQL,data_type,0.005997657775878906, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.002996683120727539, +SQL,data_type,0.005003929138183594, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004997730255126953, +SQL,data_type,0.00400090217590332, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020151138305664062,392 +SQL,unique_values,0.000988006591796875,392 +SQL,unique_values,0.0010085105895996094,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.001999378204345703,392 +SQL,bar/line,0.004999637603759766,392 +SQL,bar/line,0.0059871673583984375,392 +SQL,bar/line,0.003998756408691406,392 +SQL,get_attributes,0.02399921417236328, +SQL,cardinality,0.003999471664428711, +SQL,cardinality,0.0010309219360351562, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009860992431640625, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.0039904117584228516, +SQL,data_type,0.003996372222900391, +SQL,data_type,0.0040051937103271484, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.0010013580322265625, +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.001003265380859375,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.000985860824584961,392 +SQL,data_length,0.001001596450805664,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.00098419189453125,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0020041465759277344,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00099945068359375,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0009982585906982422,392 +SQL,min_max,0.001001596450805664,392 +SQL,get_attributes,0.004001140594482422, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010173320770263672, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0040013790130615234, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0020041465759277344,392 +SQL,min_max,0.0020036697387695312,392 +SQL,scatter,0.004000425338745117,392 +SQL,get_attributes,0.02399301528930664, +SQL,cardinality,0.003997325897216797, +SQL,cardinality,0.002011537551879883, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0020034313201904297, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020003318786621094, +SQL,data_type,0.004999399185180664, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0060024261474609375, +SQL,data_type,0.003011465072631836, +SQL,data_type,0.0050067901611328125, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003997802734375, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.00400090217590332, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.002004861831665039,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0019953250885009766,392 +SQL,unique_values,0.0019943714141845703,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0029997825622558594,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0030035972595214844,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0029969215393066406,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0020017623901367188,392 +SQL,data_length,0.002000093460083008,392 +SQL,unique_values,0.0020151138305664062,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0019943714141845703,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009970664978027344,392 +SQL,min_max,0.001001119613647461,392 +SQL,get_attributes,0.003999233245849609, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009970664978027344, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.0029976367950439453, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0040018558502197266, +SQL,data_length,0.0010013580322265625, +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0010254383087158203,392 +SQL,unique_values,0.0010356903076171875,392 +SQL,unique_values,0.0009877681732177734,392 +SQL,unique_values,0.0010221004486083984,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010368824005126953,392 +SQL,min_max,0.002010822296142578,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.00103759765625,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.005999565124511719,392 +SQL,get_attributes,0.024005651473999023, +SQL,cardinality,0.0029821395874023438, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010395050048828125, +SQL,cardinality,0.0010228157043457031, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0020215511322021484, +SQL,cardinality,0.0020003318786621094, +SQL,data_type,0.0039844512939453125, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.002986907958984375, +SQL,data_type,0.0030374526977539062, +SQL,data_type,0.0039806365966796875, +SQL,data_type,0.002974271774291992, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0040378570556640625, +SQL,data_length,0.0010042190551757812, +SQL,unique_values,0.0009677410125732422,392 +SQL,unique_values,0.0009620189666748047,392 +SQL,unique_values,0.0009636878967285156,392 +SQL,unique_values,0.002008676528930664,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009822845458984375,392 +SQL,unique_values,0.0009865760803222656,392 +SQL,unique_values,0.002001047134399414,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001009225845336914,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010097026824951172,392 +SQL,data_length,0.001008749008178711,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010097026824951172,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0020084381103515625,392 +SQL,unique_values,0.001983165740966797,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010440349578857422,392 +SQL,min_max,0.0009884834289550781,392 +SQL,min_max,0.0010075569152832031,392 +SQL,min_max,0.0010366439819335938,392 +SQL,min_max,0.0009763240814208984,392 +SQL,min_max,0.0010008811950683594,392 +SQL,get_attributes,0.004001140594482422, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0010082721710205078, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010368824005126953, +SQL,cardinality,0.0010094642639160156, +SQL,cardinality,0.0009949207305908203, +SQL,cardinality,0.0010128021240234375, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0019998550415039062, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.0059964656829833984, +SQL,data_type,0.00500035285949707, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003992795944213867, +SQL,data_type,0.0039997100830078125, +SQL,data_length,0.0009987354278564453, +SQL,unique_values,0.0020036697387695312,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010170936584472656,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0020012855529785156,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.003992795944213867,392 +SQL,get_attributes,0.022999048233032227, +SQL,cardinality,0.003007650375366211, +SQL,cardinality,0.0019655227661132812, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010182857513427734, +SQL,cardinality,0.0009741783142089844, +SQL,cardinality,0.0010089874267578125, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003992319107055664, +SQL,data_type,0.0030274391174316406, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.003965854644775391, +SQL,data_length,0.001013040542602539, +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0019898414611816406,392 +SQL,unique_values,0.0020296573638916016,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0019893646240234375,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.0019986629486083984,392 +SQL,min_max,0.001998424530029297,392 +SQL,min_max,0.001999378204345703,392 +SQL,min_max,0.0010020732879638672,392 +SQL,data_length,0.0009996891021728516,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0019974708557128906,392 +SQL,unique_values,0.0019969940185546875,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010235309600830078,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001996755599975586,392 +SQL,min_max,0.0010120868682861328,392 +SQL,min_max,0.002016305923461914,392 +SQL,get_attributes,0.004000186920166016, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0020258426666259766, +SQL,cardinality,0.0009877681732177734, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0020036697387695312, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0020029544830322266, +SQL,data_type,0.003996133804321289, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.006000041961669922, +SQL,data_type,0.004998683929443359, +SQL,data_length,0.0020017623901367188, +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0019996166229248047,392 +SQL,get_attributes,0.15000033378601074, +SQL,cardinality,0.0030031204223632812, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.002003908157348633, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001997709274291992, +SQL,data_type,0.005005359649658203, +SQL,data_type,0.005001068115234375, +SQL,data_type,0.003975391387939453, +SQL,data_type,0.005006313323974609, +SQL,data_type,0.002997159957885742, +SQL,data_type,0.0070037841796875, +SQL,data_type,0.006000995635986328, +SQL,data_type,0.0050067901611328125, +SQL,data_type,0.005994558334350586, +SQL,data_length,0.0020020008087158203, +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0029993057250976562,392 +SQL,unique_values,0.002006053924560547,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001005411148071289,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.001998424530029297,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0019979476928710938,392 +SQL,data_length,0.0010008811950683594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0020253658294677734,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010089874267578125,392 +SQL,min_max,0.002003908157348633,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.002002239227294922,392 +SQL,min_max,0.0020034313201904297,392 +SQL,get_attributes,0.005000114440917969, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003001689910888672, +SQL,data_length,0.0010013580322265625, +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009837150573730469,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009996891021728516,392 +SQL,get_attributes,0.021998167037963867, +SQL,cardinality,0.004002571105957031, +SQL,cardinality,0.001996278762817383, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0029921531677246094, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003003835678100586, +SQL,data_type,0.0060002803802490234, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.004001617431640625, +SQL,data_length,0.0010027885437011719, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010073184967041016,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0020034313201904297,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010008811950683594,392 +SQL,data_length,0.0010004043579101562,392 +SQL,unique_values,0.0019855499267578125,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0020055770874023438,392 +SQL,unique_values,0.0009922981262207031,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009918212890625,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.002003192901611328,392 +SQL,min_max,0.0009918212890625,392 +SQL,min_max,0.0010018348693847656,392 +SQL,get_attributes,0.00500035285949707, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009882450103759766, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0009992122650146484, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.00599980354309082, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003999233245849609, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0020182132720947266,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010144710540771484,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009849071502685547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.000997781753540039,392 +SQL,min_max,0.0010004043579101562,392 +SQL,scatter,0.004000663757324219,392 +SQL,get_attributes,0.023025989532470703, +SQL,cardinality,0.003998279571533203, +SQL,cardinality,0.002004384994506836, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0009808540344238281, +SQL,cardinality,0.0010275840759277344, +SQL,cardinality,0.0009729862213134766, +SQL,cardinality,0.0010290145874023438, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0030248165130615234, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003010988235473633, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.006000518798828125, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.001028299331665039,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,unique_values,0.002004384994506836,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0010073184967041016,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.002001523971557617,392 +SQL,data_length,0.0010013580322265625,392 +SQL,unique_values,0.0009853839874267578,392 +SQL,unique_values,0.002004861831665039,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.002015352249145508,392 +SQL,unique_values,0.0009953975677490234,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0019919872283935547,392 +SQL,min_max,0.001990079879760742,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020020008087158203,392 +SQL,get_attributes,0.004984855651855469, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0009968280792236328, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0020034313201904297, +SQL,cardinality,0.0020074844360351562, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.001985788345336914, +SQL,data_type,0.0059850215911865234, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0029981136322021484, +SQL,data_length,0.0009989738464355469, +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010080337524414062,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0019941329956054688,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.002004861831665039,392 +SQL,min_max,0.0020024776458740234,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.0020036697387695312,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0009999275207519531,392 +SQL,get_attributes,0.020996570587158203, +SQL,cardinality,0.003000020980834961, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.004006624221801758, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0039920806884765625, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.0039975643157958984, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003998994827270508, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.0019989013671875,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.001007080078125,392 +SQL,data_length,0.0010027885437011719,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010042190551757812,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.000997781753540039,392 +SQL,min_max,0.001003265380859375,392 +SQL,get_attributes,0.0040013790130615234, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020074844360351562, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.001999378204345703, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0029976367950439453, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004007816314697266, +SQL,data_type,0.0029985904693603516, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0019989013671875,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010008811950683594,392 +SQL,binning,0.005980730056762695,392 +SQL,binning,0.003997087478637695,392 +SQL,binning,0.004999637603759766,392 +SQL,get_attributes,0.02099752426147461, +SQL,cardinality,0.0029904842376708984, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.002996206283569336, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0069997310638427734, +SQL,data_length,0.0010020732879638672, +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010116100311279297,392 +SQL,unique_values,0.0010180473327636719,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0009889602661132812,392 +SQL,min_max,0.0010008811950683594,392 +SQL,data_length,0.0010018348693847656,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.000985860824584961,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001032114028930664,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0020291805267333984,392 +SQL,min_max,0.001971006393432617,392 +SQL,min_max,0.0010004043579101562,392 +SQL,get_attributes,0.004000425338745117, +SQL,cardinality,0.0020029544830322266, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0010256767272949219, +SQL,cardinality,0.0020024776458740234, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.002032756805419922, +SQL,cardinality,0.0019979476928710938, +SQL,cardinality,0.0010039806365966797, +SQL,cardinality,0.0019996166229248047, +SQL,data_type,0.0030050277709960938, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.005001544952392578, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.006000995635986328, +SQL,data_length,0.002000570297241211, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009696483612060547,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0010306835174560547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0020008087158203125,392 +SQL,binning,0.005990266799926758,392 +SQL,binning,0.003999948501586914,392 +SQL,binning,0.00600123405456543,392 +SQL,get_attributes,0.022997140884399414, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010075569152832031, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010151863098144531, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004008769989013672, +SQL,data_type,0.003003358840942383, +SQL,data_length,0.0, +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009930133819580078,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001010894775390625,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010013580322265625,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010044574737548828,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.0010085105895996094,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.002007007598876953,392 +SQL,min_max,0.001001119613647461,392 +SQL,get_attributes,0.006003141403198242, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010192394256591797, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.002022981643676758, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.002003192901611328, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003992795944213867, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.005001068115234375, +SQL,data_length,0.0009911060333251953, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010008811950683594,392 +SQL,binning,0.007002592086791992,392 +SQL,binning,0.003970146179199219,392 +SQL,binning,0.004997968673706055,392 +SQL,get_attributes,0.023024797439575195, +SQL,cardinality,0.00299835205078125, +SQL,cardinality,0.0010142326354980469, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010311603546142578, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.002001047134399414, +SQL,data_type,0.0030286312103271484, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.004034996032714844, +SQL,data_type,0.0039713382720947266, +SQL,data_type,0.004030466079711914, +SQL,data_type,0.003969907760620117, +SQL,data_type,0.0030281543731689453, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010352134704589844,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.000997781753540039,392 +SQL,min_max,0.002000093460083008,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001001119613647461,392 +SQL,get_attributes,0.006003379821777344, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.001010894775390625, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004999399185180664, +SQL,data_type,0.003002643585205078, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.00399017333984375, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.006001710891723633, +SQL,data_length,0.000997781753540039, +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0020134449005126953,392 +SQL,unique_values,0.001997232437133789,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.001003265380859375,392 +SQL,min_max,0.0009829998016357422,392 +SQL,min_max,0.0009791851043701172,392 +SQL,bar/line,0.005000114440917969,392 +SQL,get_attributes,0.023035049438476562, +SQL,cardinality,0.003000020980834961, +SQL,cardinality,0.0019729137420654297, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.001995086669921875, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0020198822021484375, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.001001596450805664, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003003835678100586, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.004003047943115234, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.000972747802734375,392 +SQL,min_max,0.0010366439819335938,392 +SQL,min_max,0.001984119415283203,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010013580322265625,392 +SQL,data_length,0.0009987354278564453,392 +SQL,unique_values,0.001974344253540039,392 +SQL,unique_values,0.0019960403442382812,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002028942108154297,392 +SQL,unique_values,0.0020301342010498047,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010225772857666016,392 +SQL,min_max,0.0019979476928710938,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010006427764892578,392 +SQL,get_attributes,0.005001068115234375, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.000982522964477539, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009920597076416016, +SQL,cardinality,0.0009958744049072266, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.0040280818939208984, +SQL,data_type,0.004998683929443359, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.004999637603759766, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009963512420654297,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0009953975677490234,392 +SQL,bar/line,0.005998849868774414,392 +SQL,get_attributes,0.023999929428100586, +SQL,cardinality,0.0029997825622558594, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0019745826721191406, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020346641540527344, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0020012855529785156, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.0049822330474853516, +SQL,data_type,0.003969430923461914, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.005999326705932617, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.00500178337097168, +SQL,data_length,0.0010025501251220703, +SQL,unique_values,0.0020036697387695312,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010051727294921875,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.001995563507080078,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0020024776458740234,392 +SQL,min_max,0.0020029544830322266,392 +SQL,min_max,0.002003192901611328,392 +SQL,min_max,0.0019974708557128906,392 +SQL,data_length,0.0010066032409667969,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010042190551757812,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009894371032714844,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010051727294921875,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.002002239227294922,392 +SQL,min_max,0.0010018348693847656,392 +SQL,get_attributes,0.004002809524536133, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010037422180175781, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.003997802734375, +SQL,data_type,0.0030024051666259766, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0050013065338134766, +SQL,data_type,0.0030121803283691406, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0009903907775878906,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009839534759521484,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0020008087158203125,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.003998279571533203,392 +SQL,scatter,0.003997802734375,392 +SQL,get_attributes,0.025025367736816406, +SQL,cardinality,0.0019576549530029297, +SQL,cardinality,0.0010063648223876953, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.000985860824584961, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0020313262939453125, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010323524475097656, +SQL,cardinality,0.0009751319885253906, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0040416717529296875, +SQL,data_type,0.0029718875885009766, +SQL,data_type,0.003983020782470703, +SQL,data_type,0.004025697708129883, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003996849060058594, +SQL,data_length,0.002001523971557617, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0010142326354980469,392 +SQL,min_max,0.0010023117065429688,392 +SQL,data_length,0.0010018348693847656,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001005411148071289,392 +SQL,unique_values,0.0010097026824951172,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010023117065429688,392 +SQL,get_attributes,0.004000425338745117, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.001005411148071289, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009992122650146484, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.002989530563354492, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.003000974655151367, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0020041465759277344,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010123252868652344,392 +SQL,min_max,0.0009875297546386719,392 +SQL,binning,0.003000497817993164,392 +SQL,binning,0.0030019283294677734,392 +SQL,get_attributes,0.021001577377319336, +SQL,cardinality,0.002997875213623047, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0010027885437011719, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010027885437011719, +SQL,data_type,0.005001068115234375, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.004007101058959961, +SQL,data_type,0.004999637603759766, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.001007080078125,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.001013040542602539,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0010066032409667969,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0020012855529785156,392 +SQL,data_length,0.0020003318786621094,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0009832382202148438,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.001001596450805664,392 +SQL,get_attributes,0.004002809524536133, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.003996133804321289, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.005998134613037109, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.0030028820037841797, +SQL,data_type,0.002986907958984375, +SQL,data_type,0.0069730281829833984, +SQL,data_length,0.002001047134399414, +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0019989013671875,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0020055770874023438,392 +SQL,min_max,0.002010345458984375,392 +SQL,bar/line,0.005998373031616211,392 +SQL,bar/line,0.004996776580810547,392 +SQL,bar/line,0.005000591278076172,392 +SQL,get_attributes,0.023028135299682617, +SQL,cardinality,0.0029990673065185547, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0010273456573486328, +SQL,cardinality,0.0009882450103759766, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010285377502441406, +SQL,cardinality,0.0010097026824951172, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.002996683120727539, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010042190551757812,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001997232437133789,392 +SQL,min_max,0.0029985904693603516,392 +SQL,min_max,0.0030024051666259766,392 +SQL,min_max,0.0010004043579101562,392 +SQL,data_length,0.001001596450805664,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009794235229492188,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010128021240234375,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.002001523971557617,392 +SQL,get_attributes,0.0049974918365478516, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.001001596450805664, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.0029973983764648438, +SQL,data_type,0.0040056705474853516, +SQL,data_length,0.0010161399841308594, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0020046234130859375,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.002005338668823242,392 +SQL,scatter,0.0050008296966552734,392 +SQL,get_attributes,0.02302694320678711, +SQL,cardinality,0.0039997100830078125, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0019562244415283203, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020051002502441406, +SQL,data_type,0.004996061325073242, +SQL,data_type,0.0029931068420410156, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.006001472473144531, +SQL,data_type,0.00299835205078125, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.000993490219116211,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,min_max,0.002003192901611328,392 +SQL,min_max,0.0030002593994140625,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0019974708557128906,392 +SQL,data_length,0.0,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009906291961669922,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.002001523971557617,392 +SQL,get_attributes,0.0049991607666015625, +SQL,cardinality,0.002997875213623047, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0020036697387695312, +SQL,cardinality,0.001996755599975586, +SQL,cardinality,0.0010020732879638672, +SQL,data_type,0.0029904842376708984, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.0049974918365478516, +SQL,data_type,0.0030062198638916016, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.0009949207305908203,392 +SQL,unique_values,0.001992464065551758,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0009980201721191406,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.002000570297241211,392 +SQL,scatter,0.0070018768310546875,392 +SQL,scatter,0.004993438720703125,392 +SQL,get_attributes,0.024997949600219727, +SQL,cardinality,0.00499725341796875, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.001996755599975586, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0019941329956054688, +SQL,cardinality,0.0030002593994140625, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0019989013671875, +SQL,data_type,0.005997419357299805, +SQL,data_type,0.0029969215393066406, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003997325897216797, +SQL,data_type,0.005001544952392578, +SQL,data_length,0.0010020732879638672, +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009894371032714844,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.001980304718017578,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009999275207519531,392 +SQL,data_length,0.0010027885437011719,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.001005411148071289,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009951591491699219,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0010001659393310547,392 +SQL,get_attributes,0.004999399185180664, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0020182132720947266, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.004004240036010742, +SQL,data_type,0.003997087478637695, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003997802734375, +SQL,data_type,0.004998922348022461, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0030031204223632812, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0020172595977783203,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.002000570297241211,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0010001659393310547,392 +SQL,scatter,0.005002498626708984,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.005998373031616211,392 +SQL,get_attributes,0.02399730682373047, +SQL,cardinality,0.0030274391174316406, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0009729862213134766, +SQL,cardinality,0.0010349750518798828, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0030274391174316406, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0030298233032226562, +SQL,data_type,0.0039746761322021484, +SQL,data_type,0.00398564338684082, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.003973960876464844, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010149478912353516,392 +SQL,unique_values,0.0010311603546142578,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.003013134002685547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.000972747802734375,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009865760803222656,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0010006427764892578,392 +SQL,data_length,0.0010001659393310547,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.000993967056274414,392 +SQL,unique_values,0.0009951591491699219,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0009839534759521484,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0020029544830322266,392 +SQL,get_attributes,0.004001617431640625, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010035037994384766, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010142326354980469, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010056495666503906, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.005013465881347656, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0040073394775390625, +SQL,data_type,0.0030050277709960938, +SQL,data_type,0.0030083656311035156, +SQL,data_type,0.002996683120727539, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.005001544952392578, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002001523971557617,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.001007080078125,392 +SQL,min_max,0.0010023117065429688,392 +SQL,get_attributes,0.02497553825378418, +SQL,cardinality,0.003999233245849609, +SQL,cardinality,0.0010285377502441406, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0019905567169189453, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.001004934310913086, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.0020351409912109375, +SQL,cardinality,0.001999378204345703, +SQL,data_type,0.003995418548583984, +SQL,data_type,0.004968166351318359, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.003996610641479492, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004995107650756836, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.00400090217590332, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00200653076171875,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000570297241211,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.001969575881958008,392 +SQL,data_length,0.001001119613647461,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001009225845336914,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0010137557983398438,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0019989013671875,392 +SQL,min_max,0.0010023117065429688,392 +SQL,get_attributes,0.00500035285949707, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002002239227294922, +SQL,data_type,0.004008769989013672, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003993034362792969, +SQL,data_type,0.004003286361694336, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.005002498626708984, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.001996278762817383,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.002006053924560547,392 +SQL,unique_values,0.0020072460174560547,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0019884109497070312,392 +SQL,unique_values,0.000997304916381836,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0020046234130859375,392 +SQL,min_max,0.0019927024841308594,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0019986629486083984,392 +SQL,bar/line,0.00599980354309082,392 +SQL,get_attributes,0.023996353149414062, +SQL,cardinality,0.0029799938201904297, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0009729862213134766, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0010118484497070312, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010302066802978516, +SQL,cardinality,0.002003908157348633, +SQL,data_type,0.003962993621826172, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0030269622802734375, +SQL,data_type,0.003952980041503906, +SQL,data_type,0.003980875015258789, +SQL,data_type,0.0040283203125, +SQL,data_type,0.0029671192169189453, +SQL,data_type,0.0029735565185546875, +SQL,data_type,0.0029740333557128906, +SQL,data_length,0.0010023117065429688, +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0009758472442626953,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010178089141845703,392 +SQL,unique_values,0.0010287761688232422,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010037422180175781,392 +SQL,min_max,0.001991748809814453,392 +SQL,min_max,0.0009694099426269531,392 +SQL,min_max,0.002001523971557617,392 +SQL,data_length,0.0009658336639404297,392 +SQL,unique_values,0.001974821090698242,392 +SQL,unique_values,0.0020253658294677734,392 +SQL,unique_values,0.0009660720825195312,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009670257568359375,392 +SQL,min_max,0.0010294914245605469,392 +SQL,min_max,0.0010113716125488281,392 +SQL,min_max,0.0009722709655761719,392 +SQL,get_attributes,0.004029512405395508, +SQL,cardinality,0.002005338668823242, +SQL,cardinality,0.0009703636169433594, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0010218620300292969, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010039806365966797, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0029702186584472656, +SQL,data_type,0.003971099853515625, +SQL,data_type,0.0029854774475097656, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003003358840942383, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.003971576690673828, +SQL,data_type,0.004971504211425781, +SQL,data_length,0.0020003318786621094, +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.002027750015258789,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0020303726196289062,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001027822494506836,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0010077953338623047,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001993417739868164,392 +SQL,min_max,0.0010280609130859375,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0019731521606445312,392 +SQL,scatter,0.0039997100830078125,392 +SQL,get_attributes,0.12701773643493652, +SQL,cardinality,0.0029997825622558594, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0020122528076171875, +SQL,cardinality,0.0020051002502441406, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009684562683105469, +SQL,cardinality,0.0019669532775878906, +SQL,cardinality,0.002003908157348633, +SQL,data_type,0.004027843475341797, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003996372222900391, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.003031492233276367, +SQL,data_type,0.003974199295043945, +SQL,data_type,0.00399017333984375, +SQL,data_type,0.003961324691772461, +SQL,data_type,0.004015922546386719, +SQL,data_length,0.001020193099975586, +SQL,unique_values,0.0009682178497314453,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0020313262939453125,392 +SQL,unique_values,0.0019736289978027344,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0020074844360351562,392 +SQL,unique_values,0.0010287761688232422,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010013580322265625,392 +SQL,data_length,0.00099945068359375,392 +SQL,unique_values,0.0010309219360351562,392 +SQL,unique_values,0.001986265182495117,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010042190551757812,392 +SQL,unique_values,0.001024007797241211,392 +SQL,min_max,0.0009982585906982422,392 +SQL,min_max,0.0019750595092773438,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.001026153564453125,392 +SQL,min_max,0.0010037422180175781,392 +SQL,get_attributes,0.004026174545288086, +SQL,cardinality,0.001961231231689453, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0019714832305908203, +SQL,cardinality,0.001975536346435547, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004026651382446289, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.002976655960083008, +SQL,data_type,0.003997325897216797, +SQL,data_type,0.0029625892639160156, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.00400233268737793, +SQL,data_length,0.0, +SQL,unique_values,0.0009741783142089844,392 +SQL,unique_values,0.0009872913360595703,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.000997304916381836,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,min_max,0.0009753704071044922,392 +SQL,min_max,0.0009737014770507812,392 +SQL,min_max,0.0019969940185546875,392 +SQL,min_max,0.0010254383087158203,392 +SQL,min_max,0.0009996891021728516,392 +SQL,scatter,0.004971027374267578,392 +SQL,get_attributes,0.12900066375732422, +SQL,cardinality,0.0029997825622558594, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0019915103912353516, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0020036697387695312, +SQL,cardinality,0.0020024776458740234, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001001119613647461, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0050008296966552734, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.002998828887939453, +SQL,data_length,0.0009982585906982422, +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.0010199546813964844,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001008749008178711,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.002003908157348633,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001001596450805664,392 +SQL,data_length,0.001004934310913086,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009930133819580078,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009984970092773438,392 +SQL,get_attributes,0.005002260208129883, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0010116100311279297, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.001998424530029297, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0009982585906982422, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.003997087478637695, +SQL,data_type,0.005001068115234375, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0040090084075927734, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.004003047943115234, +SQL,data_type,0.003999471664428711, +SQL,data_length,0.0020017623901367188, +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0030035972595214844,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.002003192901611328,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.0019998550415039062,392 +SQL,scatter,0.006000995635986328,392 +SQL,get_attributes,0.13599801063537598, +SQL,cardinality,0.0029993057250976562, +SQL,cardinality,0.0030050277709960938, +SQL,cardinality,0.001997709274291992, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009961128234863281, +SQL,cardinality,0.0010046958923339844, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.0039958953857421875, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.0049991607666015625, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.004015207290649414, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004001140594482422, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009958744049072266,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.000993490219116211,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0009856224060058594,392 +SQL,min_max,0.0020020008087158203,392 +SQL,data_length,0.0010006427764892578,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.000989675521850586,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.000980377197265625,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0009968280792236328,392 +SQL,min_max,0.0010027885437011719,392 +SQL,get_attributes,0.004999399185180664, +SQL,cardinality,0.001993417739868164, +SQL,cardinality,0.0009822845458984375, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0, +SQL,cardinality,0.000982046127319336, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.004005908966064453, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.00401616096496582, +SQL,data_length,0.0010018348693847656, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009903907775878906,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001981973648071289,392 +SQL,unique_values,0.0009822845458984375,392 +SQL,min_max,0.0020034313201904297,392 +SQL,min_max,0.0019989013671875,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,scatter,0.005002260208129883,392 +SQL,get_attributes,0.1309983730316162, +SQL,cardinality,0.0030014514923095703, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010046958923339844, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.0019991397857666016, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.005001544952392578, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.003026247024536133, +SQL,data_type,0.003996610641479492, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.0049915313720703125, +SQL,data_type,0.004004478454589844, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0019960403442382812,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0010025501251220703,392 +SQL,data_length,0.0010030269622802734,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0010082721710205078,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0010154247283935547,392 +SQL,min_max,0.0010182857513427734,392 +SQL,get_attributes,0.0050013065338134766, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010097026824951172, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009992122650146484, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.006000995635986328, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.0030002593994140625, +SQL,data_length,0.0, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,min_max,0.0009915828704833984,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010018348693847656,392 +SQL,scatter,0.006001472473144531,392 +SQL,get_attributes,0.13497114181518555, +SQL,cardinality,0.0029981136322021484, +SQL,cardinality,0.001013040542602539, +SQL,cardinality,0.0009889602661132812, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010292530059814453, +SQL,cardinality,0.000972747802734375, +SQL,cardinality,0.0009596347808837891, +SQL,cardinality,0.001024007797241211, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004026651382446289, +SQL,data_type,0.003971099853515625, +SQL,data_type,0.004004955291748047, +SQL,data_type,0.0050280094146728516, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004973649978637695, +SQL,data_type,0.004027843475341797, +SQL,data_length,0.0009722709655761719, +SQL,unique_values,0.0020284652709960938,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.001983165740966797,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.000982046127319336,392 +SQL,min_max,0.0010259151458740234,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0009732246398925781,392 +SQL,data_length,0.0020020008087158203,392 +SQL,unique_values,0.0020036697387695312,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010297298431396484,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010287761688232422,392 +SQL,unique_values,0.0009598731994628906,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009622573852539062,392 +SQL,min_max,0.0010223388671875,392 +SQL,min_max,0.0009617805480957031,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.001001119613647461,392 +SQL,get_attributes,0.004010438919067383, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0010404586791992188, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0010318756103515625, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004030466079711914, +SQL,data_type,0.004010677337646484, +SQL,data_type,0.003989458084106445, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003973245620727539, +SQL,data_type,0.0040283203125, +SQL,data_length,0.0019516944885253906, +SQL,unique_values,0.001026153564453125,392 +SQL,unique_values,0.000988006591796875,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009737014770507812,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,scatter,0.005018711090087891,392 +SQL,get_attributes,0.13203740119934082, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0019731521606445312, +SQL,cardinality,0.0009624958038330078, +SQL,cardinality,0.0010318756103515625, +SQL,cardinality,0.001018524169921875, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.003010272979736328, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0040090084075927734, +SQL,data_type,0.003002643585205078, +SQL,data_type,0.0030126571655273438, +SQL,data_type,0.003985404968261719, +SQL,data_type,0.003021717071533203, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0019903182983398438,392 +SQL,unique_values,0.0010378360748291016,392 +SQL,unique_values,0.001013040542602539,392 +SQL,unique_values,0.0009603500366210938,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,unique_values,0.0010378360748291016,392 +SQL,unique_values,0.0010349750518798828,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009818077087402344,392 +SQL,min_max,0.001035928726196289,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0020203590393066406,392 +SQL,min_max,0.0009992122650146484,392 +SQL,data_length,0.0009961128234863281,392 +SQL,unique_values,0.0009870529174804688,392 +SQL,unique_values,0.0010223388671875,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020172595977783203,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.002041339874267578,392 +SQL,min_max,0.002001523971557617,392 +SQL,get_attributes,0.004001617431640625, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0010027885437011719, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010166168212890625, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.003997325897216797, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0060024261474609375, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0009968280792236328,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010013580322265625,392 +SQL,scatter,0.004998683929443359,392 +SQL,get_attributes,0.1329970359802246, +SQL,cardinality,0.003998517990112305, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0020034313201904297, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0020008087158203125, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.0010020732879638672, +SQL,unique_values,0.0010075569152832031,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010068416595458984,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009999275207519531,392 +SQL,data_length,0.0009980201721191406,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009834766387939453,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0009806156158447266,392 +SQL,min_max,0.002002716064453125,392 +SQL,get_attributes,0.0039980411529541016, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.002006053924560547, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010063648223876953, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.003007173538208008, +SQL,data_type,0.006003379821777344, +SQL,data_type,0.00403594970703125, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0039958953857421875, +SQL,data_type,0.00396275520324707, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.00599980354309082, +SQL,data_length,0.0, +SQL,unique_values,0.00203704833984375,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009737014770507812,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010068416595458984,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.000993490219116211,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.000997304916381836,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010111331939697266,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.0009922981262207031,392 +SQL,min_max,0.0010104179382324219,392 +SQL,scatter,0.0039997100830078125,392 +SQL,get_attributes,0.12899994850158691, +SQL,cardinality,0.0029997825622558594, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010077953338623047, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029973983764648438, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004996061325073242, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.003997325897216797, +SQL,data_type,0.004011869430541992, +SQL,data_length,0.0009958744049072266, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.001005411148071289,392 +SQL,min_max,0.001001119613647461,392 +SQL,data_length,0.0010004043579101562,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.000980377197265625,392 +SQL,get_attributes,0.0049991607666015625, +SQL,cardinality,0.001998424530029297, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.0, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.0009958744049072266, +SQL,cardinality,0.0010025501251220703, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003003358840942383, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0029969215393066406, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.005001068115234375, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009922981262207031,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010020732879638672,392 +SQL,scatter,0.006001472473144531,392 +SQL,get_attributes,0.13100957870483398, +SQL,cardinality,0.003049135208129883, +SQL,cardinality,0.0019829273223876953, +SQL,cardinality,0.0009827613830566406, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0010056495666503906, +SQL,cardinality,0.0009694099426269531, +SQL,cardinality,0.0019910335540771484, +SQL,cardinality,0.0010178089141845703, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.0030291080474853516, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.0049669742584228516, +SQL,data_type,0.0049746036529541016, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003972291946411133, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.0029726028442382812, +SQL,data_type,0.003008604049682617, +SQL,data_length,0.0010056495666503906, +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0020270347595214844,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0020334720611572266,392 +SQL,min_max,0.0009701251983642578,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010027885437011719,392 +SQL,data_length,0.001028299331665039,392 +SQL,unique_values,0.0009775161743164062,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.00102996826171875,392 +SQL,unique_values,0.0010294914245605469,392 +SQL,unique_values,0.0010058879852294922,392 +SQL,min_max,0.0010340213775634766,392 +SQL,min_max,0.002036571502685547,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.001004934310913086,392 +SQL,min_max,0.002000093460083008,392 +SQL,get_attributes,0.004981279373168945, +SQL,cardinality,0.0020341873168945312, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020055770874023438, +SQL,cardinality,0.0010259151458740234, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0009667873382568359, +SQL,cardinality,0.0, +SQL,data_type,0.003971099853515625, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.002972126007080078, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.003026723861694336, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004026889801025391, +SQL,data_length,0.0009717941284179688, +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0010280609130859375,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.00102996826171875,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010294914245605469,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.001003265380859375,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0009691715240478516,392 +SQL,scatter,0.004999876022338867,392 +SQL,get_attributes,0.12996387481689453, +SQL,cardinality,0.002969980239868164, +SQL,cardinality,0.002003192901611328, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0010068416595458984, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009670257568359375, +SQL,cardinality,0.0010027885437011719, +SQL,cardinality,0.0010287761688232422, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0040035247802734375, +SQL,data_type,0.0029952526092529297, +SQL,data_type,0.0030031204223632812, +SQL,data_type,0.004039287567138672, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.003963470458984375, +SQL,data_type,0.003027200698852539, +SQL,data_length,0.0010263919830322266, +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0030057430267333984,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0010306835174560547,392 +SQL,unique_values,0.001972198486328125,392 +SQL,unique_values,0.0010292530059814453,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010080337524414062,392 +SQL,min_max,0.0010023117065429688,392 +SQL,data_length,0.0019736289978027344,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009763240814208984,392 +SQL,unique_values,0.002004384994506836,392 +SQL,unique_values,0.001013040542602539,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,min_max,0.0009829998016357422,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.002003192901611328,392 +SQL,min_max,0.002027750015258789,392 +SQL,min_max,0.0010006427764892578,392 +SQL,get_attributes,0.004973649978637695, +SQL,cardinality,0.003027677536010742, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009856224060058594, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.0, +SQL,cardinality,0.0010366439819335938, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.0029973983764648438, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003999471664428711, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002028226852416992,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009753704071044922,392 +SQL,unique_values,0.0010275840759277344,392 +SQL,unique_values,0.001973390579223633,392 +SQL,unique_values,0.0010063648223876953,392 +SQL,unique_values,0.0009932518005371094,392 +SQL,unique_values,0.0009944438934326172,392 +SQL,min_max,0.0009737014770507812,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010035037994384766,392 +SQL,min_max,0.0009722709655761719,392 +SQL,bar/line,0.014997243881225586,392 +SQL,get_attributes,0.1330280303955078, +SQL,cardinality,0.002995014190673828, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0010135173797607422, +SQL,cardinality,0.0010280609130859375, +SQL,cardinality,0.000972747802734375, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0019741058349609375, +SQL,cardinality,0.001997709274291992, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003027200698852539, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0029976367950439453, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004029750823974609, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003000020980834961, +SQL,data_length,0.0009992122650146484, +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010309219360351562,392 +SQL,unique_values,0.0009737014770507812,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009758472442626953,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010340213775634766,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.002009868621826172,392 +SQL,data_length,0.0,392 +SQL,unique_values,0.002026081085205078,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009963512420654297,392 +SQL,unique_values,0.0009665489196777344,392 +SQL,unique_values,0.0009961128234863281,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010085105895996094,392 +SQL,min_max,0.0020363330841064453,392 +SQL,min_max,0.002028942108154297,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009913444519042969,392 +SQL,min_max,0.0009996891021728516,392 +SQL,get_attributes,0.004000425338745117, +SQL,cardinality,0.0010287761688232422, +SQL,cardinality,0.000995635986328125, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0009756088256835938, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.004025936126708984, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0029685497283935547, +SQL,data_type,0.004028797149658203, +SQL,data_type,0.005000591278076172, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.002998828887939453, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0009627342224121094,392 +SQL,unique_values,0.0010313987731933594,392 +SQL,unique_values,0.0010223388671875,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010297298431396484,392 +SQL,min_max,0.0009980201721191406,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0020303726196289062,392 +SQL,scatter,0.005026340484619141,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.0040285587310791016,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.0050051212310791016,392 +SQL,scatter,0.004027366638183594,392 +SQL,scatter,0.005033254623413086,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.003999471664428711,392 +SQL,scatter,0.00597381591796875,392 +SQL,scatter,0.005001544952392578,392 +SQL,scatter,0.0050029754638671875,392 +SQL,scatter,0.005999565124511719,392 +SQL,scatter,0.005998134613037109,392 +SQL,scatter,0.006032228469848633,392 +SQL,scatter,0.00400090217590332,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.005000591278076172,392 +SQL,binning,0.003999471664428711,392 +SQL,binning,0.0030007362365722656,392 +SQL,binning,0.0029821395874023438,392 +SQL,binning,0.003004312515258789,392 +SQL,binning,0.0030002593994140625,392 +SQL,bar/line,0.006000995635986328,392 +SQL,bar/line,0.006036520004272461,392 +SQL,bar/line,0.006999015808105469,392 +SQL,bar/line,0.0060231685638427734,392 +SQL,get_attributes,0.02700185775756836, +SQL,cardinality,0.00400233268737793, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0008742809295654297, +SQL,cardinality,0.0009968280792236328, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0020036697387695312, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0029952526092529297, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0040013790130615234, +SQL,data_length,0.0009887218475341797, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,unique_values,0.0018525123596191406,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001005411148071289,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0020029544830322266,392 +SQL,min_max,0.001001119613647461,392 +SQL,data_length,0.0010013580322265625,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.001001119613647461,392 +SQL,get_attributes,0.004001617431640625, +SQL,cardinality,0.001998424530029297, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.00103759765625, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0009992122650146484, +SQL,data_type,0.0030078887939453125, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0060100555419921875, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.005002021789550781, +SQL,data_length,0.0009992122650146484, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.00099945068359375,392 +SQL,scatter,0.006002902984619141,392 +SQL,scatter,0.0060002803802490234,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.004988670349121094,392 +SQL,scatter,0.0060007572174072266,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.00500178337097168,392 +SQL,scatter,0.00500178337097168,392 +SQL,scatter,0.004001140594482422,392 +SQL,scatter,0.003999471664428711,392 +SQL,scatter,0.003000020980834961,392 +SQL,scatter,0.003000497817993164,392 +SQL,scatter,0.003996610641479492,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.0029993057250976562,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.0030252933502197266,392 +SQL,scatter,0.0030264854431152344,392 +SQL,scatter,0.003997087478637695,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.003973960876464844,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.002998828887939453,392 +SQL,scatter,0.003997087478637695,392 +SQL,scatter,0.0040302276611328125,392 +SQL,scatter,0.005021095275878906,392 +SQL,binning,0.003000020980834961,392 +SQL,binning,0.003028392791748047,392 +SQL,scatter,0.004023313522338867,392 +SQL,get_attributes,0.021998882293701172, +SQL,cardinality,0.0030057430267333984, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.001998424530029297, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.002012491226196289, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003977537155151367, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003996610641479492, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003999471664428711, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010256767272949219,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020325183868408203,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010178089141845703,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,min_max,0.0019979476928710938,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0009644031524658203,392 +SQL,min_max,0.0009672641754150391,392 +SQL,min_max,0.0009634494781494141,392 +SQL,data_length,0.0009739398956298828,392 +SQL,unique_values,0.0010199546813964844,392 +SQL,unique_values,0.001973390579223633,392 +SQL,unique_values,0.0019741058349609375,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010268688201904297,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,min_max,0.0009682178497314453,392 +SQL,min_max,0.0019693374633789062,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010004043579101562,392 +SQL,get_attributes,0.00500035285949707, +SQL,cardinality,0.001997232437133789, +SQL,cardinality,0.0010292530059814453, +SQL,cardinality,0.0, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.0009715557098388672, +SQL,data_type,0.0029714107513427734, +SQL,data_type,0.004007577896118164, +SQL,data_type,0.003962993621826172, +SQL,data_type,0.003030538558959961, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.00400090217590332, +SQL,data_length,0.0009810924530029297, +SQL,unique_values,0.0010325908660888672,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0009925365447998047,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0019719600677490234,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.0009620189666748047,392 +SQL,min_max,0.0009698867797851562,392 +SQL,scatter,0.004026651382446289,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.00400996208190918,392 +SQL,scatter,0.003994941711425781,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.004026889801025391,392 +SQL,scatter,0.005005359649658203,392 +SQL,scatter,0.005994319915771484,392 +SQL,scatter,0.004029512405395508,392 +SQL,scatter,0.004997968673706055,392 +SQL,scatter,0.0060312747955322266,392 +SQL,scatter,0.004967927932739258,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004030466079711914,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.005001068115234375,392 +SQL,binning,0.003973960876464844,392 +SQL,binning,0.004000663757324219,392 +SQL,binning,0.003000020980834961,392 +SQL,binning,0.0030291080474853516,392 +SQL,binning,0.0030298233032226562,392 +SQL,bar/line,0.0059697628021240234,392 +SQL,bar/line,0.0060405731201171875,392 +SQL,bar/line,0.006999969482421875,392 +SQL,bar/line,0.005972146987915039,392 +SQL,scatter,0.0060002803802490234,392 +SQL,scatter,0.005029201507568359,392 +SQL,scatter,0.005973100662231445,392 +SQL,scatter,0.006024360656738281,392 +SQL,scatter,0.0060002803802490234,392 +SQL,scatter,0.0060269832611083984,392 +SQL,scatter,0.00599980354309082,392 +SQL,scatter,0.004998922348022461,392 +SQL,scatter,0.004029273986816406,392 +SQL,scatter,0.004973411560058594,392 +SQL,scatter,0.003991842269897461,392 +SQL,scatter,0.003997802734375,392 +SQL,scatter,0.0039637088775634766,392 +SQL,scatter,0.00302886962890625,392 +SQL,scatter,0.003031015396118164,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.003983974456787109,392 +SQL,scatter,0.004015207290649414,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.003003358840942383,392 +SQL,scatter,0.004003047943115234,392 +SQL,scatter,0.003999471664428711,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.003973722457885742,392 +SQL,scatter,0.0049746036529541016,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.0050089359283447266,392 +SQL,binning,0.003973245620727539,392 +SQL,binning,0.0039997100830078125,392 +SQL,scatter,0.005002737045288086,392 +SQL,get_attributes,0.023033857345581055, +SQL,cardinality,0.00197601318359375, +SQL,cardinality,0.0010080337524414062, +SQL,cardinality,0.0009946823120117188, +SQL,cardinality,0.001970052719116211, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010194778442382812, +SQL,cardinality,0.0009953975677490234, +SQL,cardinality,0.0010361671447753906, +SQL,cardinality,0.0009632110595703125, +SQL,data_type,0.004003047943115234, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.005964756011962891, +SQL,data_type,0.0039730072021484375, +SQL,data_type,0.004011392593383789, +SQL,data_type,0.0029463768005371094, +SQL,data_length,0.0009665489196777344, +SQL,unique_values,0.0020275115966796875,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0020101070404052734,392 +SQL,unique_values,0.0019876956939697266,392 +SQL,unique_values,0.0009582042694091797,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,min_max,0.0010251998901367188,392 +SQL,min_max,0.0019648075103759766,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010371208190917969,392 +SQL,min_max,0.002001047134399414,392 +SQL,data_length,0.0009710788726806641,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009546279907226562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010366439819335938,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010378360748291016,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.001004934310913086,392 +SQL,min_max,0.0009679794311523438,392 +SQL,min_max,0.0009565353393554688,392 +SQL,min_max,0.0009677410125732422,392 +SQL,min_max,0.0009818077087402344,392 +SQL,get_attributes,0.004001140594482422, +SQL,cardinality,0.0019714832305908203, +SQL,cardinality,0.0, +SQL,cardinality,0.000978708267211914, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0009746551513671875, +SQL,cardinality,0.0009753704071044922, +SQL,cardinality,0.0009636878967285156, +SQL,cardinality,0.0009832382202148438, +SQL,cardinality,0.0009646415710449219, +SQL,data_type,0.003962516784667969, +SQL,data_type,0.0029757022857666016, +SQL,data_type,0.0029747486114501953, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.004036426544189453, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.002991914749145508, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004019737243652344, +SQL,data_length,0.0009639263153076172, +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.001972675323486328,392 +SQL,unique_values,0.0010180473327636719,392 +SQL,unique_values,0.0009613037109375,392 +SQL,unique_values,0.0009584426879882812,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0009639263153076172,392 +SQL,unique_values,0.0009670257568359375,392 +SQL,unique_values,0.0010061264038085938,392 +SQL,min_max,0.0009617805480957031,392 +SQL,min_max,0.000993967056274414,392 +SQL,min_max,0.0019783973693847656,392 +SQL,min_max,0.0009965896606445312,392 +SQL,min_max,0.002016782760620117,392 +SQL,binning,0.00603032112121582,392 +SQL,binning,0.004995822906494141,392 +SQL,binning,0.004999876022338867,392 +SQL,get_attributes,0.02099752426147461, +SQL,cardinality,0.003020763397216797, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.0009627342224121094, +SQL,cardinality,0.0009844303131103516, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009632110595703125, +SQL,data_type,0.00396418571472168, +SQL,data_type,0.0040073394775390625, +SQL,data_type,0.003034830093383789, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004043102264404297, +SQL,data_type,0.002991199493408203, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0030417442321777344, +SQL,data_type,0.003995180130004883, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0010445117950439453,392 +SQL,unique_values,0.001973390579223633,392 +SQL,unique_values,0.0010139942169189453,392 +SQL,unique_values,0.0020301342010498047,392 +SQL,unique_values,0.0010194778442382812,392 +SQL,unique_values,0.0009589195251464844,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010287761688232422,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010199546813964844,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.002000093460083008,392 +SQL,data_length,0.0009982585906982422,392 +SQL,unique_values,0.0009958744049072266,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.0020046234130859375,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,unique_values,0.0019693374633789062,392 +SQL,min_max,0.0010037422180175781,392 +SQL,min_max,0.0010263919830322266,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009860992431640625,392 +SQL,min_max,0.001971721649169922,392 +SQL,get_attributes,0.005030155181884766, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009703636169433594, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0010259151458740234, +SQL,data_type,0.004031181335449219, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.004039287567138672, +SQL,data_type,0.004999637603759766, +SQL,data_type,0.0030167102813720703, +SQL,data_type,0.0040051937103271484, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0029799938201904297, +SQL,data_length,0.0009946823120117188, +SQL,unique_values,0.0010154247283935547,392 +SQL,unique_values,0.0020089149475097656,392 +SQL,unique_values,0.0009915828704833984,392 +SQL,unique_values,0.0019774436950683594,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0010058879852294922,392 +SQL,unique_values,0.0009789466857910156,392 +SQL,unique_values,0.0009756088256835938,392 +SQL,min_max,0.0009613037109375,392 +SQL,min_max,0.0010213851928710938,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009984970092773438,392 +SQL,binning,0.005974769592285156,392 +SQL,binning,0.004000186920166016,392 +SQL,binning,0.008995771408081055,392 +SQL,get_attributes,0.025000810623168945, +SQL,cardinality,0.0040013790130615234, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010037422180175781, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010023117065429688, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004004955291748047, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.002997875213623047, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0020112991333007812,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0019989013671875,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.002001523971557617,392 +SQL,data_length,0.0010013580322265625,392 +SQL,unique_values,0.002005338668823242,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0020072460174560547,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010106563568115234,392 +SQL,min_max,0.0020029544830322266,392 +SQL,min_max,0.0010008811950683594,392 +SQL,get_attributes,0.004002571105957031, +SQL,cardinality,0.002999544143676758, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.001998424530029297, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009982585906982422, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.006000041961669922, +SQL,data_type,0.004003286361694336, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004004001617431641, +SQL,data_length,0.0, +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0019834041595458984,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002008199691772461,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010051727294921875,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.0019979476928710938,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010004043579101562,392 +SQL,binning,0.004998922348022461,392 +SQL,binning,0.003998756408691406,392 +SQL,binning,0.004997968673706055,392 +SQL,get_attributes,0.022998571395874023, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0, +SQL,cardinality,0.00099945068359375, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0030035972595214844, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003974437713623047, +SQL,data_type,0.004025459289550781, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0009684562683105469,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009748935699462891,392 +SQL,unique_values,0.000995635986328125,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.0010302066802978516,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.002029895782470703,392 +SQL,min_max,0.0009756088256835938,392 +SQL,min_max,0.001005411148071289,392 +SQL,data_length,0.0010297298431396484,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009670257568359375,392 +SQL,unique_values,0.0009517669677734375,392 +SQL,unique_values,0.0009634494781494141,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009849071502685547,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010595321655273438,392 +SQL,min_max,0.0010027885437011719,392 +SQL,get_attributes,0.004982948303222656, +SQL,cardinality,0.0019979476928710938, +SQL,cardinality,0.0010035037994384766, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010075569152832031, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.001031637191772461, +SQL,cardinality,0.0010290145874023438, +SQL,data_type,0.003029346466064453, +SQL,data_type,0.005023002624511719, +SQL,data_type,0.003973960876464844, +SQL,data_type,0.005006313323974609, +SQL,data_type,0.0039975643157958984, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003982067108154297, +SQL,data_length,0.0009992122650146484, +SQL,unique_values,0.0009682178497314453,392 +SQL,unique_values,0.0020058155059814453,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0009584426879882812,392 +SQL,unique_values,0.001962900161743164,392 +SQL,unique_values,0.0019719600677490234,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,min_max,0.000997304916381836,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009822845458984375,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0009999275207519531,392 +SQL,bar/line,0.0049974918365478516,392 +SQL,get_attributes,0.020002365112304688, +SQL,cardinality,0.0029709339141845703, +SQL,cardinality,0.0010306835174560547, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0010251998901367188, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009860992431640625, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010287761688232422, +SQL,data_type,0.0040264129638671875, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0030298233032226562, +SQL,data_type,0.003026723861694336, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003974199295043945, +SQL,data_type,0.004025459289550781, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.0019712448120117188,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010063648223876953,392 +SQL,unique_values,0.0009636878967285156,392 +SQL,unique_values,0.0009653568267822266,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0019979476928710938,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010156631469726562,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.001972198486328125,392 +SQL,data_length,0.0009598731994628906,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010302066802978516,392 +SQL,unique_values,0.0019729137420654297,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009953975677490234,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,min_max,0.0010123252868652344,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010044574737548828,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009713172912597656,392 +SQL,get_attributes,0.00397181510925293, +SQL,cardinality,0.002007007598876953, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.001027822494506836, +SQL,cardinality,0.0009829998016357422, +SQL,cardinality,0.0020089149475097656, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010251998901367188, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009708404541015625, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003024578094482422, +SQL,data_type,0.006032228469848633, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004030704498291016, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003970623016357422, +SQL,data_type,0.0030341148376464844, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.0009739398956298828, +SQL,unique_values,0.0009722709655761719,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010342597961425781,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0010254383087158203,392 +SQL,min_max,0.0009663105010986328,392 +SQL,min_max,0.0009717941284179688,392 +SQL,min_max,0.0019989013671875,392 +SQL,min_max,0.0019676685333251953,392 +SQL,min_max,0.001035928726196289,392 +SQL,bar/line,0.0069997310638427734,392 +SQL,get_attributes,0.02097153663635254, +SQL,cardinality,0.0030052661895751953, +SQL,cardinality,0.0010268688201904297, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.001026153564453125, +SQL,cardinality,0.0019757747650146484, +SQL,cardinality,0.0010254383087158203, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.003975391387939453, +SQL,data_type,0.003027200698852539, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.003988981246948242, +SQL,data_type,0.0040283203125, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.002970457077026367, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.000997781753540039, +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010161399841308594,392 +SQL,unique_values,0.001026153564453125,392 +SQL,unique_values,0.0010297298431396484,392 +SQL,unique_values,0.000985860824584961,392 +SQL,unique_values,0.0009679794311523438,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0020058155059814453,392 +SQL,data_length,0.0,392 +SQL,unique_values,0.001996755599975586,392 +SQL,unique_values,0.001031637191772461,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010085105895996094,392 +SQL,unique_values,0.0009665489196777344,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0010159015655517578,392 +SQL,min_max,0.002008676528930664,392 +SQL,min_max,0.001965761184692383,392 +SQL,min_max,0.0030031204223632812,392 +SQL,min_max,0.002003192901611328,392 +SQL,min_max,0.0030014514923095703,392 +SQL,get_attributes,0.0049746036529541016, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.002003192901611328, +SQL,cardinality,0.001971006393432617, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0010063648223876953, +SQL,cardinality,0.002008199691772461, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.001010894775390625, +SQL,data_type,0.006000041961669922, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003972053527832031, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004004716873168945, +SQL,data_type,0.004001140594482422, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010340213775634766,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010213851928710938,392 +SQL,unique_values,0.0010290145874023438,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001999378204345703,392 +SQL,min_max,0.0010309219360351562,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009911060333251953,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0019996166229248047,392 +SQL,scatter,0.004030466079711914,392 +SQL,scatter,0.003997325897216797,392 +SQL,scatter,0.004967451095581055,392 +SQL,get_attributes,0.022014856338500977, +SQL,cardinality,0.0030579566955566406, +SQL,cardinality,0.002040386199951172, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0009729862213134766, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020051002502441406, +SQL,cardinality,0.0009884834289550781, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.0009715557098388672, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.003022432327270508, +SQL,data_type,0.002970457077026367, +SQL,data_type,0.0040285587310791016, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.003964662551879883, +SQL,data_type,0.0029921531677246094, +SQL,data_type,0.0050008296966552734, +SQL,data_length,0.0010313987731933594, +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0010259151458740234,392 +SQL,unique_values,0.0010287761688232422,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009717941284179688,392 +SQL,min_max,0.0010216236114501953,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.001001119613647461,392 +SQL,data_length,0.0010004043579101562,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0009639263153076172,392 +SQL,unique_values,0.0009653568267822266,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0019965171813964844,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010042190551757812,392 +SQL,get_attributes,0.005001068115234375, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.0009963512420654297, +SQL,cardinality,0.0009682178497314453, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0009808540344238281, +SQL,cardinality,0.0010275840759277344, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004971742630004883, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0029735565185546875, +SQL,data_type,0.0039746761322021484, +SQL,data_type,0.004027366638183594, +SQL,data_type,0.003030061721801758, +SQL,data_type,0.003008604049682617, +SQL,data_type,0.003000497817993164, +SQL,data_length,0.0009980201721191406, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010271072387695312,392 +SQL,unique_values,0.0009963512420654297,392 +SQL,unique_values,0.0020296573638916016,392 +SQL,unique_values,0.0010104179382324219,392 +SQL,unique_values,0.001971721649169922,392 +SQL,unique_values,0.0009961128234863281,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0010356903076171875,392 +SQL,min_max,0.0019884109497070312,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010037422180175781,392 +SQL,min_max,0.0010247230529785156,392 +SQL,binning,0.004030466079711914,392 +SQL,binning,0.0029997825622558594,392 +SQL,get_attributes,0.03103351593017578, +SQL,cardinality,0.0030155181884765625, +SQL,cardinality,0.0019698143005371094, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0009958744049072266, +SQL,cardinality,0.0009753704071044922, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.002046346664428711, +SQL,cardinality,0.0010256767272949219, +SQL,cardinality,0.0009717941284179688, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029976367950439453, +SQL,data_type,0.0029730796813964844, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003969907760620117, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.005005359649658203, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010273456573486328,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002016305923461914,392 +SQL,unique_values,0.0009829998016357422,392 +SQL,min_max,0.0010199546813964844,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010213851928710938,392 +SQL,min_max,0.0019958019256591797,392 +SQL,data_length,0.0020394325256347656,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.00102996826171875,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009958744049072266,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009715557098388672,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0009968280792236328,392 +SQL,min_max,0.0009744167327880859,392 +SQL,get_attributes,0.004995584487915039, +SQL,cardinality,0.002016305923461914, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0010323524475097656, +SQL,cardinality,0.0010311603546142578, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001969575881958008, +SQL,data_type,0.003970623016357422, +SQL,data_type,0.004029750823974609, +SQL,data_type,0.0049648284912109375, +SQL,data_type,0.0029973983764648438, +SQL,data_type,0.003972053527832031, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003027677536010742, +SQL,data_type,0.003972530364990234, +SQL,data_length,0.001001596450805664, +SQL,unique_values,0.0010366439819335938,392 +SQL,unique_values,0.001043081283569336,392 +SQL,unique_values,0.0009589195251464844,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0009608268737792969,392 +SQL,unique_values,0.0010106563568115234,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0010051727294921875,392 +SQL,min_max,0.0009710788726806641,392 +SQL,min_max,0.0009715557098388672,392 +SQL,min_max,0.0010297298431396484,392 +SQL,min_max,0.00102996826171875,392 +SQL,min_max,0.002028226852416992,392 +SQL,bar/line,0.005002737045288086,392 +SQL,bar/line,0.004995584487915039,392 +SQL,bar/line,0.005010843276977539,392 +SQL,get_attributes,0.024040937423706055, +SQL,cardinality,0.003039121627807617, +SQL,cardinality,0.0009610652923583984, +SQL,cardinality,0.001971006393432617, +SQL,cardinality,0.0009970664978027344, +SQL,cardinality,0.0, +SQL,cardinality,0.0009667873382568359, +SQL,cardinality,0.0009961128234863281, +SQL,cardinality,0.0019714832305908203, +SQL,cardinality,0.001999378204345703, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003036975860595703, +SQL,data_type,0.0029609203338623047, +SQL,data_type,0.0039958953857421875, +SQL,data_type,0.0030281543731689453, +SQL,data_type,0.004003047943115234, +SQL,data_type,0.003997802734375, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.0010266304016113281, +SQL,unique_values,0.0010290145874023438,392 +SQL,unique_values,0.0010216236114501953,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001973867416381836,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0019729137420654297,392 +SQL,min_max,0.0010273456573486328,392 +SQL,min_max,0.001970529556274414,392 +SQL,min_max,0.0020062923431396484,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.0009639263153076172,392 +SQL,unique_values,0.0019669532775878906,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0019974708557128906,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010068416595458984,392 +SQL,min_max,0.0010046958923339844,392 +SQL,min_max,0.001979351043701172,392 +SQL,min_max,0.0020003318786621094,392 +SQL,get_attributes,0.006003618240356445, +SQL,cardinality,0.0019631385803222656, +SQL,cardinality,0.0010373592376708984, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009608268737792969, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009610652923583984, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0010039806365966797, +SQL,cardinality,0.0010356903076171875, +SQL,data_type,0.0040283203125, +SQL,data_type,0.004030466079711914, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0040280818939208984, +SQL,data_type,0.003016948699951172, +SQL,data_type,0.0040285587310791016, +SQL,data_type,0.005001544952392578, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.00400090217590332, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001970529556274414,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0009744167327880859,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010285377502441406,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010364055633544922,392 +SQL,min_max,0.0010373592376708984,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.001001119613647461,392 +SQL,bar/line,0.00701904296875,392 +SQL,get_attributes,0.023972511291503906, +SQL,cardinality,0.0029942989349365234, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010135173797607422, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0019719600677490234, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0009555816650390625, +SQL,cardinality,0.0009598731994628906, +SQL,cardinality,0.0009620189666748047, +SQL,data_type,0.003970623016357422, +SQL,data_type,0.0049974918365478516, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.0010344982147216797, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009744167327880859,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0010297298431396484,392 +SQL,unique_values,0.0010297298431396484,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0009603500366210938,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0009980201721191406,392 +SQL,min_max,0.002000570297241211,392 +SQL,data_length,0.0010004043579101562,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010294914245605469,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.002026796340942383,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.00099945068359375,392 +SQL,get_attributes,0.0050008296966552734, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009794235229492188, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009698867797851562, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0009698867797851562, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003995418548583984, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.0030279159545898438, +SQL,data_type,0.003993034362792969, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003970623016357422, +SQL,data_type,0.003999233245849609, +SQL,data_length,0.0009713172912597656, +SQL,unique_values,0.001970529556274414,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.002017974853515625,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0010304450988769531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010313987731933594,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0010306835174560547,392 +SQL,scatter,0.011001348495483398,392 +SQL,scatter,0.007004737854003906,392 +SQL,get_attributes,0.0229489803314209, +SQL,cardinality,0.003000974655151367, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009837150573730469, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.000972747802734375, +SQL,cardinality,0.0010275840759277344, +SQL,cardinality,0.000972747802734375, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.003997802734375, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004026174545288086, +SQL,data_type,0.0029506683349609375, +SQL,data_type,0.003973245620727539, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.002992391586303711, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0020036697387695312,392 +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.002003908157348633,392 +SQL,unique_values,0.001024007797241211,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010263919830322266,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0010352134704589844,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010285377502441406,392 +SQL,min_max,0.0009713172912597656,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.000995635986328125,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.000965118408203125,392 +SQL,unique_values,0.00096893310546875,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0009949207305908203,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010008811950683594,392 +SQL,get_attributes,0.003998756408691406, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0010309219360351562, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009739398956298828, +SQL,cardinality,0.0010273456573486328, +SQL,cardinality,0.0009732246398925781, +SQL,data_type,0.002991914749145508, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0029850006103515625, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0040283203125, +SQL,data_type,0.003026723861694336, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002030611038208008,392 +SQL,unique_values,0.0010259151458740234,392 +SQL,unique_values,0.0010275840759277344,392 +SQL,min_max,0.0009741783142089844,392 +SQL,min_max,0.0020568370819091797,392 +SQL,min_max,0.0010263919830322266,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0020003318786621094,392 +SQL,scatter,0.006964445114135742,392 +SQL,scatter,0.0040130615234375,392 +SQL,scatter,0.004995584487915039,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.0050258636474609375,392 +SQL,scatter,0.004965066909790039,392 +SQL,scatter,0.005003213882446289,392 +SQL,get_attributes,0.025999784469604492, +SQL,cardinality,0.002998828887939453, +SQL,cardinality,0.0019979476928710938, +SQL,cardinality,0.0009970664978027344, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009989738464355469, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.005000591278076172, +SQL,data_type,0.005011558532714844, +SQL,data_type,0.007013797760009766, +SQL,data_type,0.005003452301025391, +SQL,data_type,0.005004167556762695, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003998517990112305, +SQL,data_length,0.0009987354278564453, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009953975677490234,392 +SQL,unique_values,0.0020186901092529297,392 +SQL,unique_values,0.0010104179382324219,392 +SQL,unique_values,0.0020089149475097656,392 +SQL,unique_values,0.002002239227294922,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.001997709274291992,392 +SQL,min_max,0.0010106563568115234,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0009949207305908203,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010030269622802734,392 +SQL,get_attributes,0.004999876022338867, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.005001544952392578, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.003000020980834961, +SQL,data_length,0.0009992122650146484, +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.001997709274291992,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0009982585906982422,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010013580322265625,392 +SQL,scatter,0.005999565124511719,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.0060007572174072266,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.005999326705932617,392 +SQL,scatter,0.004998922348022461,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.0050029754638671875,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.005999088287353516,392 +SQL,scatter,0.003999471664428711,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.005001544952392578,392 +SQL,scatter,0.003997087478637695,392 +SQL,scatter,0.005028724670410156,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.005011558532714844,392 +SQL,scatter,0.006002187728881836,392 +SQL,binning,0.006000518798828125,392 +SQL,binning,0.0039751529693603516,392 +SQL,binning,0.0029997825622558594,392 +SQL,binning,0.00397181510925293,392 +SQL,binning,0.005010843276977539,392 +SQL,get_attributes,0.02200007438659668, +SQL,cardinality,0.002999544143676758, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0010144710540771484, +SQL,cardinality,0.0019981861114501953, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002000570297241211, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.003997087478637695, +SQL,data_type,0.004004716873168945, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.0010013580322265625, +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.0020008087158203125,392 +SQL,scatter,0.008996725082397461,392 +SQL,get_attributes,0.019999265670776367, +SQL,cardinality,0.002997159957885742, +SQL,cardinality,0.0009944438934326172, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0020148754119873047, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.001003265380859375, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0030045509338378906, +SQL,data_length,0.0, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001004934310913086,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0020024776458740234,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.002001523971557617,392 +SQL,data_length,0.0010027885437011719,392 +SQL,unique_values,0.0010075569152832031,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001996755599975586,392 +SQL,min_max,0.0010035037994384766,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010178089141845703,392 +SQL,min_max,0.0020194053649902344,392 +SQL,get_attributes,0.005001068115234375, +SQL,cardinality,0.002017498016357422, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010066032409667969, +SQL,data_type,0.003985881805419922, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.003002166748046875, +SQL,data_type,0.004004240036010742, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003996849060058594, +SQL,data_type,0.004030942916870117, +SQL,data_length,0.002001047134399414, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020246505737304688,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,unique_values,0.0019712448120117188,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020318031311035156,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009715557098388672,392 +SQL,min_max,0.001001119613647461,392 +SQL,bar/line,0.005030393600463867,392 +SQL,bar/line,0.005999088287353516,392 +SQL,bar/line,0.005000591278076172,392 +SQL,get_attributes,0.02302718162536621, +SQL,cardinality,0.002940654754638672, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0010280609130859375, +SQL,cardinality,0.0009653568267822266, +SQL,cardinality,0.0009639263153076172, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.002019166946411133, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0029745101928710938, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003971576690673828, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.0029799938201904297, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0019731521606445312,392 +SQL,unique_values,0.0010266304016113281,392 +SQL,unique_values,0.0010142326354980469,392 +SQL,unique_values,0.0009746551513671875,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.001972198486328125,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,min_max,0.0009982585906982422,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0009720325469970703,392 +SQL,min_max,0.0010001659393310547,392 +SQL,data_length,0.0009877681732177734,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0009593963623046875,392 +SQL,unique_values,0.0009670257568359375,392 +SQL,unique_values,0.0019974708557128906,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0009715557098388672,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009989738464355469,392 +SQL,get_attributes,0.003973245620727539, +SQL,cardinality,0.002030611038208008, +SQL,cardinality,0.0010066032409667969, +SQL,cardinality,0.0009672641754150391, +SQL,cardinality,0.0009648799896240234, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0009624958038330078, +SQL,cardinality,0.0009903907775878906, +SQL,cardinality,0.0010035037994384766, +SQL,cardinality,0.0010411739349365234, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.006960630416870117, +SQL,data_type,0.003966569900512695, +SQL,data_type,0.002969026565551758, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.003971099853515625, +SQL,data_type,0.003999471664428711, +SQL,data_length,0.0, +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.001027822494506836,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.001001119613647461,392 +SQL,bar/line,0.004998445510864258,392 +SQL,bar/line,0.0049703121185302734,392 +SQL,bar/line,0.004998922348022461,392 +SQL,get_attributes,0.021027088165283203, +SQL,cardinality,0.0030364990234375, +SQL,cardinality,0.0009975433349609375, +SQL,cardinality,0.0009930133819580078, +SQL,cardinality,0.0009882450103759766, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0010213851928710938, +SQL,cardinality,0.0, +SQL,cardinality,0.001020193099975586, +SQL,data_type,0.003997802734375, +SQL,data_type,0.0029833316802978516, +SQL,data_type,0.0029592514038085938, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.003022432327270508, +SQL,data_type,0.0029706954956054688, +SQL,data_type,0.004028797149658203, +SQL,data_type,0.003981351852416992, +SQL,data_type,0.00302886962890625, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0009911060333251953,392 +SQL,unique_values,0.0009636878967285156,392 +SQL,unique_values,0.0009610652923583984,392 +SQL,unique_values,0.0009615421295166016,392 +SQL,unique_values,0.0009593963623046875,392 +SQL,unique_values,0.0009641647338867188,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.002002239227294922,392 +SQL,min_max,0.0009615421295166016,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.002009868621826172,392 +SQL,min_max,0.0009627342224121094,392 +SQL,min_max,0.00199127197265625,392 +SQL,data_length,0.0010004043579101562,392 +SQL,unique_values,0.0010423660278320312,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009899139404296875,392 +SQL,unique_values,0.0019719600677490234,392 +SQL,unique_values,0.0020303726196289062,392 +SQL,unique_values,0.0010106563568115234,392 +SQL,unique_values,0.0010094642639160156,392 +SQL,unique_values,0.0009598731994628906,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,min_max,0.0019910335540771484,392 +SQL,min_max,0.0009946823120117188,392 +SQL,min_max,0.0009958744049072266,392 +SQL,min_max,0.0009751319885253906,392 +SQL,min_max,0.001004934310913086,392 +SQL,get_attributes,0.004000186920166016, +SQL,cardinality,0.0020279884338378906, +SQL,cardinality,0.0, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0, +SQL,cardinality,0.001028299331665039, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.002997159957885742, +SQL,data_type,0.0030362606048583984, +SQL,data_type,0.003969669342041016, +SQL,data_type,0.003981828689575195, +SQL,data_type,0.004038095474243164, +SQL,data_type,0.0029964447021484375, +SQL,data_type,0.0039904117584228516, +SQL,data_type,0.002972126007080078, +SQL,data_type,0.003999233245849609, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009789466857910156,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0020041465759277344,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009827613830566406,392 +SQL,bar/line,0.014001607894897461,392 +SQL,get_attributes,0.020998001098632812, +SQL,cardinality,0.003997802734375, +SQL,cardinality,0.0019729137420654297, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.0010023117065429688, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003973484039306641, +SQL,data_type,0.004004240036010742, +SQL,data_type,0.002959728240966797, +SQL,data_type,0.0029637813568115234, +SQL,data_type,0.0029876232147216797, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003996849060058594, +SQL,data_type,0.003972530364990234, +SQL,data_length,0.0019736289978027344, +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0019838809967041016,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.002029895782470703,392 +SQL,unique_values,0.0009694099426269531,392 +SQL,unique_values,0.001966238021850586,392 +SQL,unique_values,0.001974821090698242,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0009713172912597656,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010001659393310547,392 +SQL,data_length,0.0009982585906982422,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.002015352249145508,392 +SQL,unique_values,0.0010259151458740234,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009710788726806641,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0010008811950683594,392 +SQL,get_attributes,0.004029035568237305, +SQL,cardinality,0.0019609928131103516, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010285377502441406, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0009806156158447266, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.0009670257568359375, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004045009613037109, +SQL,data_type,0.002973318099975586, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004005908966064453, +SQL,data_type,0.004002571105957031, +SQL,data_length,0.0009732246398925781, +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009768009185791016,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010073184967041016,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0010042190551757812,392 +SQL,unique_values,0.0019745826721191406,392 +SQL,unique_values,0.0010273456573486328,392 +SQL,min_max,0.0019752979278564453,392 +SQL,min_max,0.0009655952453613281,392 +SQL,min_max,0.0010042190551757812,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009703636169433594,392 +SQL,bar/line,0.011030197143554688,392 +SQL,get_attributes,0.020985126495361328, +SQL,cardinality,0.00299072265625, +SQL,cardinality,0.0019745826721191406, +SQL,cardinality,0.0009965896606445312, +SQL,cardinality,0.0009675025939941406, +SQL,cardinality,0.0009899139404296875, +SQL,cardinality,0.0010266304016113281, +SQL,cardinality,0.0009677410125732422, +SQL,cardinality,0.0009672641754150391, +SQL,cardinality,0.0009663105010986328, +SQL,data_type,0.0039937496185302734, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003972291946411133, +SQL,data_type,0.004027128219604492, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0029747486114501953, +SQL,data_type,0.003973960876464844, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.005002498626708984, +SQL,data_length,0.001026153564453125, +SQL,unique_values,0.0010259151458740234,392 +SQL,unique_values,0.0010209083557128906,392 +SQL,unique_values,0.0009722709655761719,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0009684562683105469,392 +SQL,unique_values,0.0009891986846923828,392 +SQL,unique_values,0.0009667873382568359,392 +SQL,min_max,0.0009715557098388672,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009975433349609375,392 +SQL,min_max,0.0020270347595214844,392 +SQL,data_length,0.0009748935699462891,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010132789611816406,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0020246505737304688,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001035451889038086,392 +SQL,min_max,0.0010023117065429688,392 +SQL,get_attributes,0.005971431732177734, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.002029895782470703, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.002026796340942383, +SQL,cardinality,0.0009672641754150391, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0019714832305908203, +SQL,cardinality,0.0010035037994384766, +SQL,cardinality,0.000990152359008789, +SQL,data_type,0.0050008296966552734, +SQL,data_type,0.006000041961669922, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004029273986816406, +SQL,data_type,0.0030121803283691406, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.0030269622802734375, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.0040013790130615234, +SQL,data_length,0.0020003318786621094, +SQL,unique_values,0.0019855499267578125,392 +SQL,unique_values,0.0010323524475097656,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010273456573486328,392 +SQL,unique_values,0.0019953250885009766,392 +SQL,unique_values,0.0010287761688232422,392 +SQL,unique_values,0.001050710678100586,392 +SQL,unique_values,0.0010099411010742188,392 +SQL,min_max,0.0009980201721191406,392 +SQL,min_max,0.000985860824584961,392 +SQL,min_max,0.0009737014770507812,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010008811950683594,392 +SQL,bar/line,0.007041215896606445,392 +SQL,get_attributes,0.021076679229736328, +SQL,cardinality,0.00400090217590332, +SQL,cardinality,0.001035451889038086, +SQL,cardinality,0.0009763240814208984, +SQL,cardinality,0.0009653568267822266, +SQL,cardinality,0.0010290145874023438, +SQL,cardinality,0.000965118408203125, +SQL,cardinality,0.0009968280792236328, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010213851928710938, +SQL,data_type,0.005002021789550781, +SQL,data_type,0.004036664962768555, +SQL,data_type,0.0039937496185302734, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.002971172332763672, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.0, +SQL,unique_values,0.0020089149475097656,392 +SQL,unique_values,0.0010046958923339844,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0009927749633789062,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0010111331939697266,392 +SQL,min_max,0.002035856246948242,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.001010894775390625,392 +SQL,min_max,0.0010023117065429688,392 +SQL,data_length,0.0019998550415039062,392 +SQL,unique_values,0.0009756088256835938,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010151863098144531,392 +SQL,unique_values,0.001996755599975586,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,min_max,0.0019752979278564453,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.0009965896606445312,392 +SQL,min_max,0.0009961128234863281,392 +SQL,min_max,0.0009722709655761719,392 +SQL,get_attributes,0.00497126579284668, +SQL,cardinality,0.001985788345336914, +SQL,cardinality,0.0020062923431396484, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010325908660888672, +SQL,cardinality,0.00099945068359375, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0040035247802734375, +SQL,data_type,0.0029942989349365234, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003973960876464844, +SQL,data_type,0.004038810729980469, +SQL,data_type,0.004029035568237305, +SQL,data_type,0.003962039947509766, +SQL,data_length,0.0009601116180419922, +SQL,unique_values,0.0009613037109375,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009799003601074219,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.001018524169921875,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0019745826721191406,392 +SQL,min_max,0.0009770393371582031,392 +SQL,min_max,0.000997304916381836,392 +SQL,min_max,0.0010292530059814453,392 +SQL,min_max,0.0009720325469970703,392 +SQL,min_max,0.0019731521606445312,392 +SQL,binning,0.005029439926147461,392 +SQL,get_attributes,0.024031877517700195, +SQL,cardinality,0.0029637813568115234, +SQL,cardinality,0.0009746551513671875, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010287761688232422, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010232925415039062, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.003029346466064453, +SQL,data_type,0.004034996032714844, +SQL,data_type,0.0039899349212646484, +SQL,data_type,0.0029838085174560547, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.005977630615234375, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.0010182857513427734, +SQL,unique_values,0.0019767284393310547,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0010311603546142578,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.001031637191772461,392 +SQL,min_max,0.0010073184967041016,392 +SQL,min_max,0.0009703636169433594,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.0009779930114746094,392 +SQL,min_max,0.002000093460083008,392 +SQL,data_length,0.0009989738464355469,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0009624958038330078,392 +SQL,unique_values,0.0009851455688476562,392 +SQL,unique_values,0.0009660720825195312,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009665489196777344,392 +SQL,unique_values,0.0009660720825195312,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0019986629486083984,392 +SQL,min_max,0.0019996166229248047,392 +SQL,get_attributes,0.003988504409790039, +SQL,cardinality,0.0030295848846435547, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0009875297546386719, +SQL,cardinality,0.0009813308715820312, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009682178497314453, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0010123252868652344, +SQL,data_type,0.002970457077026367, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.00296783447265625, +SQL,data_type,0.00399017333984375, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003975629806518555, +SQL,data_type,0.004016399383544922, +SQL,data_type,0.004025459289550781, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.001031637191772461,392 +SQL,unique_values,0.0010101795196533203,392 +SQL,unique_values,0.0010044574737548828,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.0019931793212890625,392 +SQL,unique_values,0.002014636993408203,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.00102996826171875,392 +SQL,min_max,0.0010342597961425781,392 +SQL,min_max,0.0009627342224121094,392 +SQL,min_max,0.0009870529174804688,392 +SQL,binning,0.0030002593994140625,392 +SQL,get_attributes,0.0209963321685791, +SQL,cardinality,0.003998756408691406, +SQL,cardinality,0.0020291805267333984, +SQL,cardinality,0.0009655952453613281, +SQL,cardinality,0.0009667873382568359, +SQL,cardinality,0.0009629726409912109, +SQL,cardinality,0.0010190010070800781, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.001007080078125, +SQL,cardinality,0.0009839534759521484, +SQL,data_type,0.003993988037109375, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0030298233032226562, +SQL,data_type,0.003003358840942383, +SQL,data_type,0.0029692649841308594, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.005005359649658203, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009891986846923828,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.001982450485229492,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.001998424530029297,392 +SQL,min_max,0.002000570297241211,392 +SQL,data_length,0.001001119613647461,392 +SQL,unique_values,0.002026796340942383,392 +SQL,unique_values,0.000995635986328125,392 +SQL,unique_values,0.0010142326354980469,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.001027822494506836,392 +SQL,unique_values,0.0010273456573486328,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0010230541229248047,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.00101470947265625,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010161399841308594,392 +SQL,get_attributes,0.005028486251831055, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010292530059814453, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003971576690673828, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.00397181510925293, +SQL,data_type,0.003972291946411133, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003997802734375, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0020341873168945312,392 +SQL,unique_values,0.0009806156158447266,392 +SQL,unique_values,0.0009808540344238281,392 +SQL,unique_values,0.0009949207305908203,392 +SQL,unique_values,0.0019719600677490234,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0009949207305908203,392 +SQL,min_max,0.0009675025939941406,392 +SQL,min_max,0.0010004043579101562,392 +SQL,bar/line,0.00896143913269043,392 +SQL,get_attributes,0.020999908447265625, +SQL,cardinality,0.004029512405395508, +SQL,cardinality,0.00102996826171875, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010302066802978516, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020313262939453125, +SQL,cardinality,0.0010223388671875, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0019719600677490234, +SQL,data_type,0.0039713382720947266, +SQL,data_type,0.0030324459075927734, +SQL,data_type,0.003997802734375, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0030274391174316406, +SQL,data_type,0.0029959678649902344, +SQL,data_type,0.0029714107513427734, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.00299835205078125, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010128021240234375,392 +SQL,unique_values,0.0019724369049072266,392 +SQL,unique_values,0.002004861831665039,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010216236114501953,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010256767272949219,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010285377502441406,392 +SQL,min_max,0.0009768009185791016,392 +SQL,data_length,0.0010018348693847656,392 +SQL,unique_values,0.0010294914245605469,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0009899139404296875,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.0020096302032470703,392 +SQL,unique_values,0.0009779930114746094,392 +SQL,unique_values,0.0009829998016357422,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0019741058349609375,392 +SQL,min_max,0.0009987354278564453,392 +SQL,get_attributes,0.004999399185180664, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0009703636169433594, +SQL,cardinality,0.0010259151458740234, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0009875297546386719, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.000986337661743164, +SQL,cardinality,0.0009632110595703125, +SQL,data_type,0.0029954910278320312, +SQL,data_type,0.003971099853515625, +SQL,data_type,0.004029035568237305, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003997802734375, +SQL,data_type,0.004029989242553711, +SQL,data_type,0.0030057430267333984, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0029997825622558594, +SQL,data_length,0.0009989738464355469, +SQL,unique_values,0.0009696483612060547,392 +SQL,unique_values,0.001997232437133789,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010139942169189453,392 +SQL,unique_values,0.0010113716125488281,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020313262939453125,392 +SQL,min_max,0.0010178089141845703,392 +SQL,min_max,0.0009982585906982422,392 +SQL,min_max,0.002000570297241211,392 +SQL,bar/line,0.009973526000976562,392 +SQL,get_attributes,0.021027326583862305, +SQL,cardinality,0.003000020980834961, +SQL,cardinality,0.001991748809814453, +SQL,cardinality,0.0019729137420654297, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0009882450103759766, +SQL,cardinality,0.002000570297241211, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.003982067108154297, +SQL,data_type,0.005033969879150391, +SQL,data_type,0.0039708614349365234, +SQL,data_type,0.0039713382720947266, +SQL,data_type,0.003002166748046875, +SQL,data_type,0.002974271774291992, +SQL,data_type,0.003983497619628906, +SQL,data_type,0.0029916763305664062, +SQL,data_length,0.001973867416381836, +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.001016855239868164,392 +SQL,unique_values,0.0020279884338378906,392 +SQL,unique_values,0.001972675323486328,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.002026796340942383,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,min_max,0.0009970664978027344,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.002026796340942383,392 +SQL,min_max,0.0009703636169433594,392 +SQL,min_max,0.0009717941284179688,392 +SQL,data_length,0.0009982585906982422,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010313987731933594,392 +SQL,unique_values,0.0,392 +SQL,min_max,0.0009737014770507812,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009720325469970703,392 +SQL,min_max,0.0010051727294921875,392 +SQL,min_max,0.0009729862213134766,392 +SQL,get_attributes,0.004000186920166016, +SQL,cardinality,0.002028226852416992, +SQL,cardinality,0.0009739398956298828, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010271072387695312, +SQL,cardinality,0.0009739398956298828, +SQL,cardinality,0.0010085105895996094, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009968280792236328, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004030704498291016, +SQL,data_type,0.003975391387939453, +SQL,data_type,0.004019737243652344, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.00400996208190918, +SQL,data_type,0.0029871463775634766, +SQL,data_type,0.003998517990112305, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0010488033294677734,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009722709655761719,392 +SQL,unique_values,0.0009613037109375,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0020062923431396484,392 +SQL,unique_values,0.001008749008178711,392 +SQL,unique_values,0.0009665489196777344,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010263919830322266,392 +SQL,min_max,0.0010285377502441406,392 +SQL,scatter,0.004972934722900391,392 +SQL,scatter,0.003996849060058594,392 +SQL,scatter,0.004029989242553711,392 +SQL,bar/line,0.004999399185180664,392 +SQL,bar/line,0.0060045719146728516,392 +SQL,scatter,0.005000591278076172,392 +SQL,get_attributes,0.13296890258789062, +SQL,cardinality,0.0029969215393066406, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009930133819580078, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009961128234863281, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003992557525634766, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004005908966064453, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003001689910888672, +SQL,data_type,0.004004001617431641, +SQL,data_length,0.0010013580322265625, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.003000974655151367,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0020046234130859375,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.00099945068359375,392 +SQL,data_length,0.0010013580322265625,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.002003192901611328,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.002004861831665039,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010004043579101562,392 +SQL,get_attributes,0.006000995635986328, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001018524169921875, +SQL,cardinality,0.0010089874267578125, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0030012130737304688, +SQL,data_length,0.0009996891021728516, +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009818077087402344,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.002007722854614258,392 +SQL,min_max,0.0010001659393310547,392 +SQL,scatter,0.0040018558502197266,392 +SQL,scatter,0.0050029754638671875,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.004001617431640625,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.006000995635986328,392 +SQL,scatter,0.003997087478637695,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.005006074905395508,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.00400090217590332,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.005000114440917969,392 +SQL,binning,0.0029990673065185547,392 +SQL,binning,0.0029976367950439453,392 +SQL,binning,0.002999544143676758,392 +SQL,binning,0.004000186920166016,392 +SQL,binning,0.004000663757324219,392 +SQL,bar/line,0.008980035781860352,392 +SQL,bar/line,0.008002996444702148,392 +SQL,bar/line,0.01099848747253418,392 +SQL,bar/line,0.008000373840332031,392 +SQL,scatter,0.005002498626708984,392 +SQL,scatter,0.005972862243652344,392 +SQL,scatter,0.008996009826660156,392 +SQL,scatter,0.004996538162231445,392 +SQL,scatter,0.004996538162231445,392 +SQL,scatter,0.00599980354309082,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.003998994827270508,392 +SQL,scatter,0.005003929138183594,392 +SQL,scatter,0.003997087478637695,392 +SQL,scatter,0.0050029754638671875,392 +SQL,scatter,0.003992557525634766,392 +SQL,scatter,0.0049970149993896484,392 +SQL,scatter,0.003998279571533203,392 +SQL,scatter,0.008002042770385742,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.004997968673706055,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.0059969425201416016,392 +SQL,scatter,0.00399470329284668,392 +SQL,binning,0.0070035457611083984,392 +SQL,binning,0.003998994827270508,392 +SQL,binning,0.004997730255126953,392 +SQL,binning,0.005000114440917969,392 +SQL,binning,0.006001472473144531,392 +SQL,get_attributes,0.03399515151977539, +SQL,cardinality,0.0050013065338134766, +SQL,cardinality,0.0030012130737304688, +SQL,cardinality,0.003004312515258789, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010042190551757812, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.002000093460083008, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004996776580810547, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.005000591278076172, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.007000446319580078, +SQL,data_type,0.005999088287353516, +SQL,data_type,0.005997896194458008, +SQL,data_length,0.0020051002502441406, +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010190010070800781,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009894371032714844,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0010035037994384766,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0020036697387695312,392 +SQL,min_max,0.0010082721710205078,392 +SQL,data_length,0.001001119613647461,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.002001047134399414,392 +SQL,get_attributes,0.004001140594482422, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.002992391586303711, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003983020782470703, +SQL,data_type,0.005001544952392578, +SQL,data_type,0.0029964447021484375, +SQL,data_type,0.00400543212890625, +SQL,data_length,0.001001596450805664, +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0010063648223876953,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.002015352249145508,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009634494781494141,392 +SQL,min_max,0.001003265380859375,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0020389556884765625,392 +SQL,min_max,0.0009748935699462891,392 +SQL,min_max,0.0009996891021728516,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.004963397979736328,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.005002260208129883,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.0029997825622558594,392 +SQL,scatter,0.0029747486114501953,392 +SQL,scatter,0.003000020980834961,392 +SQL,scatter,0.004002809524536133,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.003998756408691406,392 +SQL,scatter,0.0029997825622558594,392 +SQL,scatter,0.003036022186279297,392 +SQL,scatter,0.003973484039306641,392 +SQL,scatter,0.003000974655151367,392 +SQL,scatter,0.003000020980834961,392 +SQL,scatter,0.004032135009765625,392 +SQL,scatter,0.003963470458984375,392 +SQL,scatter,0.005037069320678711,392 +SQL,scatter,0.0039632320404052734,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.003998994827270508,392 +SQL,scatter,0.005000114440917969,392 +SQL,binning,0.0029990673065185547,392 +SQL,binning,0.0029973983764648438,392 +SQL,scatter,0.0040018558502197266,392 +SQL,get_attributes,0.021999120712280273, +SQL,cardinality,0.0030083656311035156, +SQL,cardinality,0.0009424686431884766, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010037422180175781, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.001001596450805664, +SQL,data_type,0.004015207290649414, +SQL,data_type,0.00498652458190918, +SQL,data_type,0.0060100555419921875, +SQL,data_type,0.004964113235473633, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.003997325897216797, +SQL,data_type,0.003037691116333008, +SQL,data_type,0.002988100051879883, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010154247283935547,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.000957489013671875,392 +SQL,unique_values,0.0009920597076416016,392 +SQL,unique_values,0.001039743423461914,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.002037525177001953,392 +SQL,min_max,0.0009732246398925781,392 +SQL,min_max,0.0010004043579101562,392 +SQL,data_length,0.0010001659393310547,392 +SQL,unique_values,0.0010399818420410156,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010113716125488281,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.002001523971557617,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009868144989013672,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010018348693847656,392 +SQL,get_attributes,0.00500035285949707, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010035037994384766, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009903907775878906, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010004043579101562, +SQL,get_attributes,0.023999452590942383, +SQL,cardinality,0.004001140594482422, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0019745826721191406, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.001972675323486328, +SQL,cardinality,0.001007080078125, +SQL,cardinality,0.0010027885437011719, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.00403141975402832, +SQL,data_type,0.002973318099975586, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.002973318099975586, +SQL,data_type,0.004972696304321289, +SQL,data_type,0.004996299743652344, +SQL,data_type,0.006000518798828125, +SQL,data_length,0.001976490020751953, +SQL,unique_values,0.001997709274291992,392 +SQL,unique_values,0.001961231231689453,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.002038717269897461,392 +SQL,unique_values,0.0009605884552001953,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.001979827880859375,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0020322799682617188,392 +SQL,min_max,0.0010039806365966797,392 +SQL,min_max,0.0009729862213134766,392 +SQL,min_max,0.0009729862213134766,392 +SQL,min_max,0.0009987354278564453,392 +SQL,data_length,0.0020008087158203125,392 +SQL,unique_values,0.001997232437133789,392 +SQL,unique_values,0.0020329952239990234,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0020036697387695312,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,unique_values,0.001997232437133789,392 +SQL,unique_values,0.0020432472229003906,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.0020051002502441406,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0020017623901367188,392 +SQL,min_max,0.002008676528930664,392 +SQL,get_attributes,0.006996631622314453, +SQL,cardinality,0.001989126205444336, +SQL,cardinality,0.001996278762817383, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009996891021728516, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003989458084106445, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003998279571533203, +SQL,data_length,0.001001596450805664, +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,min_max,0.0009975433349609375,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0019998550415039062,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.006000518798828125,392 +SQL,scatter,0.004998922348022461,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.004997968673706055,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.005999565124511719,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004987478256225586,392 +SQL,scatter,0.004001140594482422,392 +SQL,scatter,0.006998300552368164,392 +SQL,scatter,0.006999969482421875,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.006999492645263672,392 +SQL,scatter,0.00600123405456543,392 +SQL,scatter,0.004999876022338867,392 +SQL,binning,0.0050048828125,392 +SQL,binning,0.004000186920166016,392 +SQL,binning,0.004001140594482422,392 +SQL,binning,0.004001140594482422,392 +SQL,binning,0.0060083866119384766,392 +SQL,bar/line,0.010002613067626953,392 +SQL,bar/line,0.014001131057739258,392 +SQL,bar/line,0.0070002079010009766,392 +SQL,bar/line,0.0060002803802490234,392 +SQL,get_attributes,0.1480236053466797, +SQL,cardinality,0.002027750015258789, +SQL,cardinality,0.0009746551513671875, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0010063648223876953, +SQL,cardinality,0.000990152359008789, +SQL,cardinality,0.000985860824584961, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.004007816314697266, +SQL,data_type,0.003997802734375, +SQL,data_type,0.00397181510925293, +SQL,data_type,0.0029685497283935547, +SQL,data_type,0.003976106643676758, +SQL,data_type,0.004022836685180664, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004961490631103516, +SQL,data_type,0.004009246826171875, +SQL,data_length,0.0, +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009806156158447266,392 +SQL,unique_values,0.002029895782470703,392 +SQL,unique_values,0.0019712448120117188,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002032041549682617,392 +SQL,unique_values,0.0019731521606445312,392 +SQL,min_max,0.0010275840759277344,392 +SQL,min_max,0.0009698867797851562,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010256767272949219,392 +SQL,data_length,0.0009658336639404297,392 +SQL,unique_values,0.000942230224609375,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0019788742065429688,392 +SQL,unique_values,0.002026796340942383,392 +SQL,unique_values,0.0020296573638916016,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0010175704956054688,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0010275840759277344,392 +SQL,min_max,0.001001119613647461,392 +SQL,get_attributes,0.005027294158935547, +SQL,cardinality,0.0020296573638916016, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.00101470947265625, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010080337524414062, +SQL,cardinality,0.0009627342224121094, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.001003265380859375, +SQL,data_type,0.003996133804321289, +SQL,data_type,0.004019260406494141, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.003011465072631836, +SQL,data_type,0.002969980239868164, +SQL,data_type,0.003019571304321289, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003999233245849609, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0010061264038085938,392 +SQL,unique_values,0.0020148754119873047,392 +SQL,unique_values,0.0009584426879882812,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020296573638916016,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.000997304916381836,392 +SQL,min_max,0.0009725093841552734,392 +SQL,scatter,0.004971742630004883,392 +SQL,scatter,0.0040283203125,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.004007577896118164,392 +SQL,scatter,0.004967927932739258,392 +SQL,scatter,0.0059969425201416016,392 +SQL,scatter,0.0040361881256103516,392 +SQL,scatter,0.005998134613037109,392 +SQL,scatter,0.005026578903198242,392 +SQL,scatter,0.005029916763305664,392 +SQL,scatter,0.003997802734375,392 +SQL,scatter,0.004991054534912109,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.006001710891723633,392 +SQL,scatter,0.00402379035949707,392 +SQL,scatter,0.0049741268157958984,392 +SQL,scatter,0.004026651382446289,392 +SQL,scatter,0.004025697708129883,392 +SQL,binning,0.00400090217590332,392 +SQL,binning,0.00400996208190918,392 +SQL,binning,0.0029985904693603516,392 +SQL,binning,0.0050127506256103516,392 +SQL,binning,0.0030260086059570312,392 +SQL,bar/line,0.008001089096069336,392 +SQL,bar/line,0.005031108856201172,392 +SQL,bar/line,0.005991935729980469,392 +SQL,bar/line,0.0060007572174072266,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.003973960876464844,392 +SQL,scatter,0.004997730255126953,392 +SQL,scatter,0.006026029586791992,392 +SQL,scatter,0.005995512008666992,392 +SQL,scatter,0.009997367858886719,392 +SQL,scatter,0.005006551742553711,392 +SQL,scatter,0.005002498626708984,392 +SQL,scatter,0.003998994827270508,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.00699615478515625,392 +SQL,scatter,0.005035877227783203,392 +SQL,scatter,0.006028652191162109,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.005994558334350586,392 +SQL,scatter,0.004969120025634766,392 +SQL,scatter,0.006003141403198242,392 +SQL,scatter,0.006000995635986328,392 +SQL,binning,0.005000114440917969,392 +SQL,binning,0.003000497817993164,392 +SQL,binning,0.0040013790130615234,392 +SQL,binning,0.003000497817993164,392 +SQL,binning,0.0039751529693603516,392 +SQL,get_attributes,0.026012659072875977, +SQL,cardinality,0.0029990673065185547, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010271072387695312, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010302066802978516, +SQL,cardinality,0.0009641647338867188, +SQL,cardinality,0.001003265380859375, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.004994392395019531, +SQL,data_type,0.004007816314697266, +SQL,data_type,0.0039670467376708984, +SQL,data_type,0.002972841262817383, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.00502777099609375, +SQL,data_type,0.003972291946411133, +SQL,data_type,0.0030291080474853516, +SQL,data_type,0.004026889801025391, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0020051002502441406,392 +SQL,unique_values,0.0010228157043457031,392 +SQL,unique_values,0.001009225845336914,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020051002502441406,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,min_max,0.0009815692901611328,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0010113716125488281,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.001043558120727539,392 +SQL,data_length,0.0009903907775878906,392 +SQL,unique_values,0.0010409355163574219,392 +SQL,unique_values,0.001041412353515625,392 +SQL,unique_values,0.0009629726409912109,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009884834289550781,392 +SQL,unique_values,0.0010287761688232422,392 +SQL,unique_values,0.0009500980377197266,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010426044464111328,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009829998016357422,392 +SQL,min_max,0.0009999275207519531,392 +SQL,get_attributes,0.003999471664428711, +SQL,cardinality,0.0019752979278564453, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.001003265380859375, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010263919830322266, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0009703636169433594, +SQL,cardinality,0.0010273456573486328, +SQL,data_type,0.0030264854431152344, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0049703121185302734, +SQL,data_type,0.006028890609741211, +SQL,data_type,0.0039713382720947266, +SQL,data_type,0.004030942916870117, +SQL,data_type,0.0029692649841308594, +SQL,data_length,0.0009701251983642578, +SQL,unique_values,0.0020189285278320312,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,unique_values,0.0010170936584472656,392 +SQL,unique_values,0.0010302066802978516,392 +SQL,unique_values,0.0010342597961425781,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009906291961669922,392 +SQL,min_max,0.0010304450988769531,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0010006427764892578,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.008001565933227539,392 +SQL,scatter,0.005028963088989258,392 +SQL,scatter,0.0049707889556884766,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.005016326904296875,392 +SQL,scatter,0.003967761993408203,392 +SQL,scatter,0.002998828887939453,392 +SQL,scatter,0.004026174545288086,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.0030012130737304688,392 +SQL,scatter,0.004029273986816406,392 +SQL,scatter,0.0029997825622558594,392 +SQL,scatter,0.0030045509338378906,392 +SQL,scatter,0.003994941711425781,392 +SQL,scatter,0.003999471664428711,392 +SQL,scatter,0.003998994827270508,392 +SQL,scatter,0.004007101058959961,392 +SQL,scatter,0.003964900970458984,392 +SQL,scatter,0.0040018558502197266,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.0039637088775634766,392 +SQL,scatter,0.0039713382720947266,392 +SQL,scatter,0.004029989242553711,392 +SQL,scatter,0.003036975860595703,392 +SQL,scatter,0.0039975643157958984,392 +SQL,binning,0.0030069351196289062,392 +SQL,binning,0.002977609634399414,392 +SQL,scatter,0.003996610641479492,392 +SQL,get_attributes,0.1600022315979004, +SQL,cardinality,0.004004001617431641, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0019979476928710938, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.001996755599975586, +SQL,cardinality,0.0020017623901367188, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004994630813598633, +SQL,data_type,0.005995035171508789, +SQL,data_type,0.004008293151855469, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004997730255126953, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0019986629486083984,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.002002239227294922,392 +SQL,data_length,0.0009992122650146484,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010170936584472656,392 +SQL,unique_values,0.001990795135498047,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0009768009185791016,392 +SQL,unique_values,0.001998424530029297,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.0020194053649902344,392 +SQL,min_max,0.0010008811950683594,392 +SQL,get_attributes,0.006996870040893555, +SQL,cardinality,0.004000186920166016, +SQL,cardinality,0.001018524169921875, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.006000518798828125, +SQL,data_type,0.004003286361694336, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.001010894775390625,392 +SQL,scatter,0.005997419357299805,392 +SQL,scatter,0.0069997310638427734,392 +SQL,scatter,0.007973909378051758,392 +SQL,scatter,0.005029439926147461,392 +SQL,scatter,0.0050013065338134766,392 +SQL,scatter,0.006011247634887695,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.004990577697753906,392 +SQL,scatter,0.004958152770996094,392 +SQL,scatter,0.0060422420501708984,392 +SQL,scatter,0.004961490631103516,392 +SQL,scatter,0.005995988845825195,392 +SQL,scatter,0.0049893856048583984,392 +SQL,scatter,0.00497126579284668,392 +SQL,scatter,0.004973649978637695,392 +SQL,scatter,0.005995988845825195,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.004999876022338867,392 +SQL,binning,0.006998777389526367,392 +SQL,binning,0.00403285026550293,392 +SQL,binning,0.003969669342041016,392 +SQL,binning,0.00400233268737793,392 +SQL,binning,0.0039997100830078125,392 +SQL,get_attributes,0.023993730545043945, +SQL,cardinality,0.003996610641479492, +SQL,cardinality,0.0009975433349609375, +SQL,cardinality,0.001010894775390625, +SQL,cardinality,0.0009891986846923828, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.002008199691772461, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.002984285354614258, +SQL,data_type,0.0030035972595214844, +SQL,data_type,0.005002260208129883, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0040018558502197266, +SQL,data_length,0.000997781753540039, +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0010216236114501953,392 +SQL,min_max,0.0010077953338623047,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.001031637191772461,392 +SQL,min_max,0.0009679794311523438,392 +SQL,min_max,0.0010001659393310547,392 +SQL,data_length,0.0010008811950683594,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0020329952239990234,392 +SQL,unique_values,0.002005338668823242,392 +SQL,unique_values,0.0009675025939941406,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0010139942169189453,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0009963512420654297,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010018348693847656,392 +SQL,get_attributes,0.004000186920166016, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0020051002502441406, +SQL,cardinality,0.0010027885437011719, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003003835678100586, +SQL,data_type,0.005004167556762695, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0029909610748291016, +SQL,data_type,0.003000974655151367, +SQL,data_length,0.0010013580322265625, +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.0010190010070800781,392 +SQL,unique_values,0.000997304916381836,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,min_max,0.0020029544830322266,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009851455688476562,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0020012855529785156,392 +SQL,bar/line,0.012991905212402344,392 +SQL,get_attributes,0.027997493743896484, +SQL,cardinality,0.0029969215393066406, +SQL,cardinality,0.0010356903076171875, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009739398956298828, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010290145874023438, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0029954910278320312, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004002809524536133, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.003000497817993164, +SQL,data_length,0.0010085105895996094, +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.002002239227294922,392 +SQL,min_max,0.0019996166229248047,392 +SQL,min_max,0.002001523971557617,392 +SQL,data_length,0.001986265182495117,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001994609832763672,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0020356178283691406,392 +SQL,unique_values,0.0019960403442382812,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.001003265380859375,392 +SQL,min_max,0.0010046958923339844,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.002021312713623047,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0010001659393310547,392 +SQL,get_attributes,0.0039997100830078125, +SQL,cardinality,0.0010306835174560547, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009911060333251953, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0020279884338378906, +SQL,data_type,0.0030031204223632812, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0029838085174560547, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.00600123405456543, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0020301342010498047,392 +SQL,unique_values,0.001997709274291992,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010044574737548828,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010113716125488281,392 +SQL,min_max,0.0010001659393310547,392 +SQL,bar/line,0.017020702362060547,392 +SQL,bar/line,0.05400395393371582,392 +SQL,get_attributes,0.031020641326904297, +SQL,cardinality,0.0040340423583984375, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0009808540344238281, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003003358840942383, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.002994537353515625, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.00400233268737793, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.0010211467742919922,392 +SQL,unique_values,0.0010192394256591797,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0019986629486083984,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.001016855239868164,392 +SQL,data_length,0.0010001659393310547,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009932518005371094,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0009975433349609375,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010039806365966797,392 +SQL,get_attributes,0.003998756408691406, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.002001523971557617, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009996891021728516, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0029969215393066406, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009920597076416016,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.002001523971557617,392 +SQL,scatter,0.0060307979583740234,392 +SQL,scatter,0.0059740543365478516,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.0070285797119140625,392 +SQL,scatter,0.006999492645263672,392 +SQL,scatter,0.005970478057861328,392 +SQL,scatter,0.006999492645263672,392 +SQL,scatter,0.005974292755126953,392 +SQL,scatter,0.004998207092285156,392 +SQL,scatter,0.005031108856201172,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.0050013065338134766,392 +SQL,scatter,0.006000518798828125,392 +SQL,scatter,0.00503087043762207,392 +SQL,scatter,0.004972219467163086,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.004998683929443359,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004000663757324219,392 +SQL,binning,0.0070018768310546875,392 +SQL,binning,0.003999233245849609,392 +SQL,binning,0.0040013790130615234,392 +SQL,binning,0.0030012130737304688,392 +SQL,binning,0.004001617431640625,392 +SQL,get_attributes,0.030997514724731445, +SQL,cardinality,0.005999565124511719, +SQL,cardinality,0.002030611038208008, +SQL,cardinality,0.002030611038208008, +SQL,cardinality,0.0020034313201904297, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010159015655517578, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.004029273986816406, +SQL,data_type,0.0030121803283691406, +SQL,data_type,0.004029273986816406, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0039708614349365234, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0040013790130615234, +SQL,data_length,0.0009720325469970703, +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0010197162628173828,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010213851928710938,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010104179382324219,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.002003908157348633,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.000982522964477539,392 +SQL,data_length,0.0010006427764892578,392 +SQL,unique_values,0.001996755599975586,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0010030269622802734,392 +SQL,min_max,0.0019986629486083984,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.002000570297241211,392 +SQL,get_attributes,0.004001617431640625, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009849071502685547, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.000986337661743164, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0050008296966552734, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003000974655151367, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004001617431640625, +SQL,data_length,0.0009982585906982422, +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.001013040542602539,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010020732879638672,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.005002021789550781,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.006001472473144531,392 +SQL,scatter,0.00499725341796875,392 +SQL,scatter,0.005002260208129883,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.005002021789550781,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.005998373031616211,392 +SQL,scatter,0.005997896194458008,392 +SQL,scatter,0.006999969482421875,392 +SQL,scatter,0.005002498626708984,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.005002260208129883,392 +SQL,binning,0.005000114440917969,392 +SQL,binning,0.0030007362365722656,392 +SQL,binning,0.0030007362365722656,392 +SQL,binning,0.002999544143676758,392 +SQL,binning,0.005000591278076172,392 +SQL,get_attributes,0.12599968910217285, +SQL,cardinality,0.0029990673065185547, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002008199691772461, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010187625885009766, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.002989530563354492, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.003998517990112305, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010061264038085938,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0010006427764892578,392 +SQL,data_length,0.0009989738464355469,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019884109497070312,392 +SQL,unique_values,0.0009961128234863281,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010139942169189453,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010051727294921875,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,min_max,0.0009922981262207031,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0009844303131103516,392 +SQL,get_attributes,0.004000186920166016, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.002002716064453125, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.00500035285949707, +SQL,data_type,0.005002737045288086, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003001689910888672, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009942054748535156,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.001003265380859375,392 +SQL,min_max,0.0009703636169433594,392 +SQL,min_max,0.0009961128234863281,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0020051002502441406,392 +SQL,min_max,0.0020017623901367188,392 +SQL,scatter,0.005998373031616211,392 +SQL,scatter,0.005998849868774414,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.005002021789550781,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.004012584686279297,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.003998994827270508,392 +SQL,scatter,0.005009174346923828,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.004994392395019531,392 +SQL,scatter,0.004998683929443359,392 +SQL,scatter,0.0039899349212646484,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.003997802734375,392 +SQL,scatter,0.004995822906494141,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.003999948501586914,392 +SQL,binning,0.0049970149993896484,392 +SQL,binning,0.0029892921447753906,392 +SQL,binning,0.0030014514923095703,392 +SQL,binning,0.003000020980834961,392 +SQL,binning,0.004999876022338867,392 +SQL,get_attributes,0.13203787803649902, +SQL,cardinality,0.004036664962768555, +SQL,cardinality,0.001995563507080078, +SQL,cardinality,0.001026153564453125, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0009765625, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.000972747802734375, +SQL,data_type,0.002969503402709961, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003997087478637695, +SQL,data_type,0.0050013065338134766, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003029346466064453, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.0040013790130615234, +SQL,data_length,0.000972747802734375, +SQL,unique_values,0.001026153564453125,392 +SQL,unique_values,0.0010259151458740234,392 +SQL,unique_values,0.000982522964477539,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009753704071044922,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0010068416595458984,392 +SQL,unique_values,0.0009953975677490234,392 +SQL,min_max,0.0009670257568359375,392 +SQL,min_max,0.0019719600677490234,392 +SQL,min_max,0.002031087875366211,392 +SQL,min_max,0.0009720325469970703,392 +SQL,min_max,0.0010006427764892578,392 +SQL,data_length,0.0009708404541015625,392 +SQL,unique_values,0.0010302066802978516,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001031637191772461,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0010132789611816406,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0010325908660888672,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010135173797607422,392 +SQL,get_attributes,0.004029512405395508, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0019714832305908203, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.0030298233032226562, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003973722457885742, +SQL,data_type,0.0040302276611328125, +SQL,data_type,0.004027366638183594, +SQL,data_type,0.003997325897216797, +SQL,data_type,0.005002498626708984, +SQL,data_type,0.0049991607666015625, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.0010042190551757812, +SQL,unique_values,0.001996278762817383,392 +SQL,unique_values,0.0009834766387939453,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0010294914245605469,392 +SQL,unique_values,0.0019636154174804688,392 +SQL,unique_values,0.0009653568267822266,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,min_max,0.0009813308715820312,392 +SQL,min_max,0.0010275840759277344,392 +SQL,min_max,0.001026153564453125,392 +SQL,min_max,0.0019948482513427734,392 +SQL,min_max,0.0009999275207519531,392 +SQL,scatter,0.004994869232177734,392 +SQL,scatter,0.003998756408691406,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.00496983528137207,392 +SQL,scatter,0.005005836486816406,392 +SQL,scatter,0.003984212875366211,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.0049970149993896484,392 +SQL,scatter,0.005002737045288086,392 +SQL,scatter,0.0050013065338134766,392 +SQL,scatter,0.004998207092285156,392 +SQL,scatter,0.0050373077392578125,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.00503087043762207,392 +SQL,scatter,0.004973649978637695,392 +SQL,scatter,0.0040264129638671875,392 +SQL,scatter,0.005014181137084961,392 +SQL,scatter,0.005002737045288086,392 +SQL,scatter,0.0049893856048583984,392 +SQL,binning,0.00600123405456543,392 +SQL,binning,0.0029938220977783203,392 +SQL,binning,0.004004240036010742,392 +SQL,binning,0.0039997100830078125,392 +SQL,binning,0.0050008296966552734,392 +SQL,get_attributes,0.13403534889221191, +SQL,cardinality,0.0030264854431152344, +SQL,cardinality,0.001032114028930664, +SQL,cardinality,0.0010290145874023438, +SQL,cardinality,0.0010297298431396484, +SQL,cardinality,0.0009698867797851562, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0020122528076171875, +SQL,cardinality,0.002000570297241211, +SQL,data_type,0.0040302276611328125, +SQL,data_type,0.003973245620727539, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0039730072021484375, +SQL,data_type,0.0040280818939208984, +SQL,data_type,0.0029900074005126953, +SQL,data_length,0.0010085105895996094, +SQL,unique_values,0.0019598007202148438,392 +SQL,unique_values,0.0010352134704589844,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.0009663105010986328,392 +SQL,unique_values,0.0010399818420410156,392 +SQL,unique_values,0.0009963512420654297,392 +SQL,unique_values,0.0010311603546142578,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0010318756103515625,392 +SQL,data_length,0.0010304450988769531,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009713172912597656,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.001008749008178711,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009672641754150391,392 +SQL,unique_values,0.0009670257568359375,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0009927749633789062,392 +SQL,get_attributes,0.005026578903198242, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010073184967041016, +SQL,cardinality,0.0010294914245605469, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009927749633789062, +SQL,cardinality,0.0010144710540771484, +SQL,cardinality,0.0010151863098144531, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.003970146179199219, +SQL,data_type,0.002971172332763672, +SQL,data_type,0.003976583480834961, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004026889801025391, +SQL,data_type,0.002969980239868164, +SQL,data_type,0.003998756408691406, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010061264038085938,392 +SQL,unique_values,0.0009663105010986328,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009636878967285156,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009715557098388672,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.002007722854614258,392 +SQL,scatter,0.005026102066040039,392 +SQL,scatter,0.004971981048583984,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004998922348022461,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.005026578903198242,392 +SQL,scatter,0.00497126579284668,392 +SQL,scatter,0.004965066909790039,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.003999471664428711,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.004002094268798828,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.003957033157348633,392 +SQL,scatter,0.0050013065338134766,392 +SQL,scatter,0.005029439926147461,392 +SQL,scatter,0.004974365234375,392 +SQL,binning,0.007002115249633789,392 +SQL,binning,0.0029997825622558594,392 +SQL,binning,0.004002094268798828,392 +SQL,binning,0.00400233268737793,392 +SQL,binning,0.003998994827270508,392 +SQL,get_attributes,0.15000176429748535, +SQL,cardinality,0.002997159957885742, +SQL,cardinality,0.0019910335540771484, +SQL,cardinality,0.0009930133819580078, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0019981861114501953, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020089149475097656, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.004999637603759766, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010213851928710938,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.001005411148071289,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,data_length,0.0010066032409667969,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.001976490020751953,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,min_max,0.0019986629486083984,392 +SQL,min_max,0.001998424530029297,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.001997232437133789,392 +SQL,get_attributes,0.006997346878051758, +SQL,cardinality,0.004000663757324219, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0020024776458740234, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.0049974918365478516, +SQL,data_type,0.0049974918365478516, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.005005598068237305, +SQL,data_type,0.00399327278137207, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.00397944450378418, +SQL,data_type,0.0040035247802734375, +SQL,data_type,0.0029993057250976562, +SQL,data_length,0.001997709274291992, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009768009185791016,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001988649368286133,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010006427764892578,392 +SQL,scatter,0.007003307342529297,392 +SQL,scatter,0.005002737045288086,392 +SQL,scatter,0.004950523376464844,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.0059888362884521484,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.004995584487915039,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.0069997310638427734,392 +SQL,scatter,0.005007505416870117,392 +SQL,scatter,0.00600123405456543,392 +SQL,scatter,0.005999565124511719,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.0050013065338134766,392 +SQL,scatter,0.005002021789550781,392 +SQL,scatter,0.006999015808105469,392 +SQL,scatter,0.005997896194458008,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.005997180938720703,392 +SQL,scatter,0.004997730255126953,392 +SQL,binning,0.004997968673706055,392 +SQL,binning,0.0040013790130615234,392 +SQL,binning,0.003000974655151367,392 +SQL,binning,0.0029997825622558594,392 +SQL,binning,0.003999233245849609,392 +SQL,get_attributes,0.13700199127197266, +SQL,cardinality,0.00299835205078125, +SQL,cardinality,0.001967191696166992, +SQL,cardinality,0.0010046958923339844, +SQL,cardinality,0.0010344982147216797, +SQL,cardinality,0.0009629726409912109, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0020241737365722656, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.005997419357299805, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0050046443939208984, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.002996206283569336, +SQL,data_type,0.003996372222900391, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003974437713623047, +SQL,data_type,0.0039942264556884766, +SQL,data_length,0.0009706020355224609, +SQL,unique_values,0.0010075569152832031,392 +SQL,unique_values,0.001005411148071289,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.00104522705078125,392 +SQL,min_max,0.0010056495666503906,392 +SQL,min_max,0.0009675025939941406,392 +SQL,min_max,0.0010366439819335938,392 +SQL,min_max,0.0020415782928466797,392 +SQL,data_length,0.001001596450805664,392 +SQL,unique_values,0.0009653568267822266,392 +SQL,unique_values,0.0020258426666259766,392 +SQL,unique_values,0.0009756088256835938,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019712448120117188,392 +SQL,unique_values,0.0010290145874023438,392 +SQL,unique_values,0.0010280609130859375,392 +SQL,unique_values,0.0009891986846923828,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0010254383087158203,392 +SQL,min_max,0.0009713172912597656,392 +SQL,min_max,0.0009992122650146484,392 +SQL,get_attributes,0.004001617431640625, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009953975677490234, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010406970977783203, +SQL,cardinality,0.0009622573852539062, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.0030181407928466797, +SQL,data_type,0.003973722457885742, +SQL,data_type,0.0030024051666259766, +SQL,data_length,0.0009729862213134766, +SQL,unique_values,0.0009739398956298828,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009844303131103516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020263195037841797,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,min_max,0.0009970664978027344,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009691715240478516,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.0009987354278564453,392 +SQL,scatter,0.0060024261474609375,392 +SQL,scatter,0.007999420166015625,392 +SQL,scatter,0.005999326705932617,392 +SQL,scatter,0.0060002803802490234,392 +SQL,scatter,0.00499725341796875,392 +SQL,scatter,0.004001140594482422,392 +SQL,scatter,0.009002685546875,392 +SQL,scatter,0.007002592086791992,392 +SQL,scatter,0.005003929138183594,392 +SQL,scatter,0.004997968673706055,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.0059986114501953125,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.00599980354309082,392 +SQL,scatter,0.0039975643157958984,392 +SQL,scatter,0.005001544952392578,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.0050029754638671875,392 +SQL,scatter,0.005003213882446289,392 +SQL,scatter,0.004999399185180664,392 +SQL,binning,0.007010459899902344,392 +SQL,binning,0.003999948501586914,392 +SQL,binning,0.005001544952392578,392 +SQL,binning,0.003000020980834961,392 +SQL,binning,0.007014751434326172,392 +SQL,get_attributes,0.13097047805786133, +SQL,cardinality,0.0029921531677246094, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0010313987731933594, +SQL,cardinality,0.0010304450988769531, +SQL,cardinality,0.00102996826171875, +SQL,cardinality,0.0019719600677490234, +SQL,cardinality,0.0010242462158203125, +SQL,cardinality,0.0009765625, +SQL,cardinality,0.001001119613647461, +SQL,data_type,0.005000591278076172, +SQL,data_type,0.004029273986816406, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.0049664974212646484, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.002989530563354492, +SQL,data_type,0.003962278366088867, +SQL,data_type,0.0029616355895996094, +SQL,data_length,0.0009603500366210938, +SQL,unique_values,0.002029895782470703,392 +SQL,unique_values,0.0010294914245605469,392 +SQL,unique_values,0.0009722709655761719,392 +SQL,unique_values,0.0009737014770507812,392 +SQL,unique_values,0.0009722709655761719,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010066032409667969,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.0010173320770263672,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.001013040542602539,392 +SQL,min_max,0.0010399818420410156,392 +SQL,min_max,0.0010285377502441406,392 +SQL,data_length,0.000972747802734375,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.00096893310546875,392 +SQL,unique_values,0.0010132789611816406,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0010073184967041016,392 +SQL,unique_values,0.0010151863098144531,392 +SQL,unique_values,0.0010058879852294922,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.001032114028930664,392 +SQL,min_max,0.0009975433349609375,392 +SQL,min_max,0.0009729862213134766,392 +SQL,min_max,0.0009722709655761719,392 +SQL,get_attributes,0.005001544952392578, +SQL,cardinality,0.0020291805267333984, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.001012563705444336, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0009894371032714844, +SQL,cardinality,0.000997781753540039, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.0030295848846435547, +SQL,data_type,0.002969503402709961, +SQL,data_type,0.0039708614349365234, +SQL,data_type,0.0029692649841308594, +SQL,data_type,0.003999948501586914, +SQL,data_length,0.0009696483612060547, +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009696483612060547,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010263919830322266,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,min_max,0.0010292530059814453,392 +SQL,min_max,0.0010237693786621094,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0010306835174560547,392 +SQL,min_max,0.0019998550415039062,392 +SQL,scatter,0.004973649978637695,392 +SQL,scatter,0.005026817321777344,392 +SQL,scatter,0.004974842071533203,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.004971027374267578,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.004988908767700195,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.003995418548583984,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.005001544952392578,392 +SQL,scatter,0.005029201507568359,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.004998683929443359,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.005007028579711914,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.0040283203125,392 +SQL,scatter,0.004998922348022461,392 +SQL,binning,0.004999637603759766,392 +SQL,binning,0.002994060516357422,392 +SQL,binning,0.003999948501586914,392 +SQL,binning,0.0039751529693603516,392 +SQL,binning,0.004025459289550781,392 +SQL,get_attributes,0.12399888038635254, +SQL,cardinality,0.003027200698852539, +SQL,cardinality,0.0010280609130859375, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0020110607147216797, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010342597961425781, +SQL,cardinality,0.0009768009185791016, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0009996891021728516, +SQL,data_type,0.002992868423461914, +SQL,data_type,0.004987955093383789, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004008769989013672, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.004007101058959961, +SQL,data_type,0.004007577896118164, +SQL,data_type,0.003983497619628906, +SQL,data_length,0.001024007797241211, +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.001996278762817383,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.002017974853515625,392 +SQL,unique_values,0.000988006591796875,392 +SQL,unique_values,0.0019769668579101562,392 +SQL,unique_values,0.0009899139404296875,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010216236114501953,392 +SQL,min_max,0.0009839534759521484,392 +SQL,min_max,0.0009970664978027344,392 +SQL,min_max,0.001998424530029297,392 +SQL,min_max,0.0020029544830322266,392 +SQL,min_max,0.0009856224060058594,392 +SQL,data_length,0.0009872913360595703,392 +SQL,unique_values,0.0019927024841308594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.00101470947265625,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009949207305908203,392 +SQL,unique_values,0.0009953975677490234,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001003265380859375,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009949207305908203,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.002012968063354492,392 +SQL,min_max,0.0020325183868408203,392 +SQL,get_attributes,0.004000186920166016, +SQL,cardinality,0.0020041465759277344, +SQL,cardinality,0.0010182857513427734, +SQL,cardinality,0.0009961128234863281, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010027885437011719, +SQL,cardinality,0.001008749008178711, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.002000570297241211, +SQL,data_type,0.0029900074005126953, +SQL,data_type,0.0040051937103271484, +SQL,data_type,0.0030112266540527344, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003983020782470703, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.0019850730895996094, +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.002009153366088867,392 +SQL,unique_values,0.000988006591796875,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.0009951591491699219,392 +SQL,unique_values,0.0009877681732177734,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,min_max,0.001986980438232422,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0009958744049072266,392 +SQL,min_max,0.0009999275207519531,392 +SQL,scatter,0.0060024261474609375,392 +SQL,scatter,0.005018711090087891,392 +SQL,scatter,0.0059967041015625,392 +SQL,scatter,0.007010221481323242,392 +SQL,scatter,0.00499272346496582,392 +SQL,scatter,0.006000995635986328,392 +SQL,scatter,0.006001949310302734,392 +SQL,scatter,0.005997896194458008,392 +SQL,scatter,0.005998373031616211,392 +SQL,scatter,0.006000995635986328,392 +SQL,scatter,0.005999326705932617,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.005999088287353516,392 +SQL,scatter,0.007996320724487305,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.005997419357299805,392 +SQL,scatter,0.004998683929443359,392 +SQL,scatter,0.006001710891723633,392 +SQL,scatter,0.0050013065338134766,392 +SQL,binning,0.0049991607666015625,392 +SQL,binning,0.0030269622802734375,392 +SQL,binning,0.0029997825622558594,392 +SQL,binning,0.003986358642578125,392 +SQL,binning,0.003998756408691406,392 +SQL,get_attributes,0.12800002098083496, +SQL,cardinality,0.0019974708557128906, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0020008087158203125, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.004999399185180664, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0050008296966552734, +SQL,data_type,0.005997896194458008, SQL,data_type,0.002998828887939453, -SQL,data_type,0.003002166748046875, -SQL,data_type,0.0030007362365722656, -SQL,data_type,0.0039598941802978516, -SQL,data_type,0.002994060516357422, -SQL,data_type,0.0040171146392822266, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0009815692901611328,392 +SQL,unique_values,0.0020041465759277344,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001003265380859375,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010008811950683594,392 +SQL,data_length,0.00099945068359375,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0019986629486083984,392 +SQL,min_max,0.0010018348693847656,392 +SQL,get_attributes,0.00500035285949707, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010030269622802734, +SQL,cardinality,0.0010120868682861328, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010161399841308594, +SQL,cardinality,0.002003192901611328, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029735565185546875, +SQL,data_type,0.006000041961669922, +SQL,data_type,0.004007577896118164, +SQL,data_type,0.003958225250244141, +SQL,data_type,0.003997325897216797, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.0009984970092773438, +SQL,unique_values,0.001997709274291992,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0020110607147216797,392 +SQL,unique_values,0.0029990673065185547,392 +SQL,unique_values,0.0020360946655273438,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0020029544830322266,392 +SQL,min_max,0.002000570297241211,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.004998207092285156,392 +SQL,scatter,0.005998849868774414,392 +SQL,scatter,0.0050013065338134766,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.006000518798828125,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.008002758026123047,392 +SQL,scatter,0.00599980354309082,392 +SQL,scatter,0.003999471664428711,392 +SQL,binning,0.006011486053466797,392 +SQL,binning,0.003997087478637695,392 +SQL,binning,0.0029630661010742188,392 +SQL,binning,0.004000425338745117,392 +SQL,binning,0.004000186920166016,392 +SQL,get_attributes,0.1270005702972412, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010197162628173828, +SQL,cardinality,0.0009927749633789062, +SQL,cardinality,0.0009982585906982422, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000186920166016, SQL,data_type,0.004000663757324219, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.00400090217590332, +SQL,data_length,0.0010001659393310547, +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.001972198486328125,392 +SQL,unique_values,0.0009927749633789062,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0019860267639160156,392 +SQL,unique_values,0.0019876956939697266,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001001119613647461,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.001999378204345703,392 +SQL,min_max,0.000988006591796875,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0020012855529785156,392 +SQL,data_length,0.001009225845336914,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009789466857910156,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0020139217376708984,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009999275207519531,392 +SQL,get_attributes,0.005001068115234375, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009903907775878906, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.00400233268737793, +SQL,data_type,0.002995014190673828, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004991769790649414, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0019974708557128906,392 +SQL,unique_values,0.0010356903076171875,392 +SQL,unique_values,0.0,392 +SQL,min_max,0.001008749008178711,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0020020008087158203,392 +SQL,min_max,0.0009922981262207031,392 +SQL,scatter,0.0050013065338134766,392 +SQL,scatter,0.0060002803802490234,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.0070421695709228516,392 +SQL,scatter,0.006005048751831055,392 +SQL,scatter,0.0059964656829833984,392 +SQL,scatter,0.0049626827239990234,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.005009174346923828,392 +SQL,scatter,0.008999109268188477,392 +SQL,scatter,0.006000518798828125,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.005013942718505859,392 +SQL,scatter,0.003995656967163086,392 +SQL,scatter,0.0059986114501953125,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.0049991607666015625,392 +SQL,scatter,0.00600123405456543,392 +SQL,scatter,0.0050008296966552734,392 +SQL,binning,0.006008625030517578,392 +SQL,binning,0.003962278366088867,392 +SQL,binning,0.0030002593994140625,392 +SQL,binning,0.003993988037109375,392 +SQL,binning,0.0039997100830078125,392 +SQL,get_attributes,0.12299776077270508, +SQL,cardinality,0.0029976367950439453, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0009932518005371094, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010020732879638672, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.004997730255126953, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.0029997825622558594, +SQL,data_length,0.000997781753540039, +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0030019283294677734,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010042190551757812,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009832382202148438,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010006427764892578,392 +SQL,data_length,0.0010001659393310547,392 +SQL,unique_values,0.0020020008087158203,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.002008676528930664,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0020182132720947266,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.00099945068359375,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010039806365966797,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.000985860824584961,392 +SQL,min_max,0.001001596450805664,392 +SQL,get_attributes,0.005001544952392578, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020017623901367188, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004002571105957031, +SQL,data_type,0.00500035285949707, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.0030019283294677734, SQL,data_type,0.003002166748046875, -SQL,data_type,0.0040280818939208984, -SQL,data_type,0.003973960876464844, -SQL,data_length,1.0710387229919434, -SQL,unique_values,0.22799944877624512,855632 -SQL,unique_values,0.2039933204650879,855632 -SQL,unique_values,0.16900134086608887,855632 -SQL,unique_values,0.19300055503845215,855632 -SQL,unique_values,0.2109992504119873,855632 -SQL,unique_values,0.24501585960388184,855632 -SQL,unique_values,0.24298477172851562,855632 -SQL,unique_values,0.2200484275817871,855632 -SQL,unique_values,0.21899843215942383,855632 -SQL,unique_values,0.2610325813293457,855632 -SQL,unique_values,0.2160017490386963,855632 -SQL,min_max,0.7019977569580078,855632 -SQL,min_max,0.37699198722839355,855632 -SQL,min_max,0.7890007495880127,855632 -SQL,min_max,1.1259708404541016,855632 -SQL,scatter,2.002000331878662,855632 -SQL,scatter,2.028999090194702,855632 -SQL,scatter,1.9170153141021729,855632 -SQL,scatter,2.456651210784912,855632 -SQL,scatter,1.2779710292816162,855632 -SQL,scatter,1.7619969844818115,855632 -SQL,scatter,2.119966506958008,855632 -SQL,scatter,1.835998296737671,855632 -SQL,scatter,1.9359958171844482,855632 -SQL,scatter,2.2759954929351807,855632 -SQL,scatter,1.4490406513214111,855632 -SQL,scatter,2.474003553390503,855632 -SQL,binning,1.6050374507904053,855632 -SQL,binning,1.5199933052062988,855632 -SQL,binning,1.8660304546356201,855632 -SQL,binning,2.2249794006347656,855632 -SQL,bar/line,2.020970106124878,855632 -SQL,bar/line,1.3449654579162598,855632 -SQL,bar/line,1.7890357971191406,855632 -SQL,bar/line,1.884040117263794,855632 -SQL,bar/line,1.0639941692352295,855632 -SQL,bar/line,1.1079692840576172,855632 -SQL,bar/line,1.3920071125030518,855632 -SQL,data_length,0.2279646396636963, -SQL,get_attributes,0.07999944686889648, -SQL,cardinality,0.039000511169433594, -SQL,cardinality,0.0020008087158203125, -SQL,cardinality,0.0020084381103515625, +SQL,data_type,0.003002643585205078, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.0040283203125, +SQL,data_length,0.0009989738464355469, +SQL,unique_values,0.001972675323486328,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010271072387695312,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002001523971557617,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.001981973648071289,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0009987354278564453,392 +SQL,scatter,0.0050029754638671875,392 +SQL,scatter,0.005999326705932617,392 +SQL,scatter,0.005998134613037109,392 +SQL,scatter,0.00500178337097168,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.00400090217590332,392 +SQL,scatter,0.0059986114501953125,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.0050008296966552734,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.005001544952392578,392 +SQL,scatter,0.003998517990112305,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.003980398178100586,392 +SQL,scatter,0.005994081497192383,392 +SQL,binning,0.005000114440917969,392 +SQL,binning,0.0030007362365722656,392 +SQL,binning,0.002999544143676758,392 +SQL,binning,0.004001140594482422,392 +SQL,binning,0.003999948501586914,392 +SQL,binning,0.0030193328857421875,392 +SQL,binning,0.0029981136322021484,392 +SQL,binning,0.0029993057250976562,392 +SQL,binning,0.003001689910888672,392 +SQL,binning,0.002999544143676758,392 +SQL,bar/line,0.01099848747253418,392 +SQL,bar/line,0.009998559951782227,392 +SQL,bar/line,0.010003805160522461,392 +SQL,bar/line,0.005998134613037109,392 +SQL,bar/line,0.005999565124511719,392 +SQL,bar/line,0.005999326705932617,392 +SQL,get_attributes,0.13502979278564453, +SQL,cardinality,0.00302886962890625, +SQL,cardinality,0.001977682113647461, +SQL,cardinality,0.0019812583923339844, +SQL,cardinality,0.0020029544830322266, SQL,cardinality,0.002000570297241211, -SQL,cardinality,0.0010161399841308594, -SQL,cardinality,0.00099945068359375, -SQL,cardinality,0.0020275115966796875, -SQL,cardinality,0.0009696483612060547, -SQL,cardinality,0.001008749008178711, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.001998424530029297, +SQL,cardinality,0.0020008087158203125, +SQL,data_type,0.004999637603759766, +SQL,data_type,0.005970478057861328, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0030019283294677734, +SQL,data_type,0.0030269622802734375, +SQL,data_type,0.004025697708129883, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004000186920166016, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0019745826721191406,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.001976490020751953,392 +SQL,min_max,0.0009951591491699219,392 +SQL,min_max,0.001001119613647461,392 +SQL,data_length,0.002039670944213867,392 +SQL,unique_values,0.0009686946868896484,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0020046234130859375,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.000980377197265625,392 +SQL,unique_values,0.00103759765625,392 +SQL,min_max,0.0009641647338867188,392 +SQL,min_max,0.0009624958038330078,392 +SQL,min_max,0.000980377197265625,392 +SQL,min_max,0.0019598007202148438,392 +SQL,min_max,0.002003192901611328,392 +SQL,get_attributes,0.004999637603759766, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0020177364349365234, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009613037109375, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0009639263153076172, +SQL,cardinality,0.0009636878967285156, +SQL,cardinality,0.0009627342224121094, +SQL,cardinality,0.0010013580322265625, +SQL,data_type,0.0030384063720703125, +SQL,data_type,0.0030345916748046875, +SQL,data_type,0.0029807090759277344, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.003993511199951172, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.004001617431640625, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.0009777545928955078,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.001026153564453125,392 +SQL,unique_values,0.0009913444519042969,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010380744934082031,392 +SQL,unique_values,0.0010256767272949219,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,min_max,0.0010075569152832031,392 +SQL,min_max,0.0019989013671875,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.0009756088256835938,392 +SQL,min_max,0.0009744167327880859,392 +SQL,scatter,0.0049631595611572266,392 +SQL,scatter,0.004039287567138672,392 +SQL,scatter,0.0050029754638671875,392 +SQL,scatter,0.004024982452392578,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.004981517791748047,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.004037141799926758,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.0049932003021240234,392 +SQL,scatter,0.0050051212310791016,392 +SQL,scatter,0.0059659481048583984,392 +SQL,scatter,0.005009174346923828,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.003998994827270508,392 +SQL,scatter,0.005028724670410156,392 +SQL,scatter,0.005038261413574219,392 +SQL,scatter,0.004999399185180664,392 +SQL,scatter,0.004997730255126953,392 +SQL,binning,0.003974199295043945,392 +SQL,binning,0.0030188560485839844,392 +SQL,binning,0.004006147384643555,392 +SQL,binning,0.0030024051666259766,392 +SQL,binning,0.003000497817993164,392 +SQL,bar/line,0.006000518798828125,392 +SQL,bar/line,0.007035255432128906,392 +SQL,bar/line,0.006994962692260742,392 +SQL,bar/line,0.0059986114501953125,392 +SQL,scatter,0.00500035285949707,392 +SQL,scatter,0.006012439727783203,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.004037380218505859,392 +SQL,scatter,0.007959127426147461,392 +SQL,scatter,0.004994869232177734,392 +SQL,scatter,0.003998517990112305,392 +SQL,scatter,0.005036830902099609,392 +SQL,scatter,0.00599980354309082,392 +SQL,scatter,0.004992246627807617,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.006012678146362305,392 +SQL,scatter,0.0070247650146484375,392 +SQL,scatter,0.005006551742553711,392 +SQL,scatter,0.0049932003021240234,392 +SQL,scatter,0.005000591278076172,392 +SQL,scatter,0.004961490631103516,392 +SQL,scatter,0.006044626235961914,392 +SQL,scatter,0.0059947967529296875,392 +SQL,scatter,0.005998373031616211,392 +SQL,binning,0.0060002803802490234,392 +SQL,binning,0.003998756408691406,392 +SQL,binning,0.0040013790130615234,392 +SQL,binning,0.004037618637084961,392 +SQL,binning,0.003987550735473633,392 +SQL,binning,0.004000663757324219,392 +SQL,binning,0.003000497817993164,392 +SQL,binning,0.003001689910888672,392 +SQL,binning,0.0030269622802734375,392 +SQL,binning,0.0029997825622558594,392 +SQL,bar/line,0.01002955436706543,392 +SQL,bar/line,0.011969566345214844,392 +SQL,bar/line,0.005029439926147461,392 +SQL,bar/line,0.007999897003173828,392 +SQL,bar/line,0.006000995635986328,392 +SQL,bar/line,0.006000518798828125,392 +SQL,get_attributes,0.023991107940673828, +SQL,cardinality,0.0029976367950439453, +SQL,cardinality,0.0009644031524658203, +SQL,cardinality,0.0020279884338378906, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0009665489196777344, +SQL,cardinality,0.0010366439819335938, +SQL,cardinality,0.0010263919830322266, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0029840469360351562, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.0039942264556884766, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.003013134002685547, +SQL,data_type,0.003999233245849609, +SQL,data_length,0.0009984970092773438, +SQL,unique_values,0.002026081085205078,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.001016378402709961,392 +SQL,unique_values,0.0009646415710449219,392 +SQL,unique_values,0.001026153564453125,392 +SQL,unique_values,0.00199127197265625,392 +SQL,unique_values,0.0020034313201904297,392 +SQL,unique_values,0.0019681453704833984,392 +SQL,unique_values,0.0010058879852294922,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009839534759521484,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009839534759521484,392 +SQL,data_length,0.0009746551513671875,392 +SQL,unique_values,0.001997232437133789,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020177364349365234,392 +SQL,unique_values,0.0010254383087158203,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010242462158203125,392 +SQL,min_max,0.0010311603546142578,392 +SQL,min_max,0.0009717941284179688,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001971006393432617,392 +SQL,min_max,0.0010256767272949219,392 +SQL,get_attributes,0.004999637603759766, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.001028299331665039, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.002003908157348633, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009992122650146484, +SQL,data_type,0.0029680728912353516, +SQL,data_type,0.003965854644775391, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0029616355895996094, +SQL,data_type,0.002974271774291992, +SQL,data_type,0.002974987030029297, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003972291946411133, +SQL,data_length,0.0009725093841552734, +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009722709655761719,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.0019750595092773438,392 +SQL,unique_values,0.0010039806365966797,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.001039266586303711,392 +SQL,unique_values,0.0010371208190917969,392 +SQL,min_max,0.000995635986328125,392 +SQL,min_max,0.0009572505950927734,392 +SQL,min_max,0.0010306835174560547,392 +SQL,min_max,0.001973867416381836,392 +SQL,min_max,0.0010268688201904297,392 +SQL,scatter,0.0050013065338134766,392 +SQL,scatter,0.004998683929443359,392 +SQL,scatter,0.004029273986816406,392 +SQL,scatter,0.004998922348022461,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.0060007572174072266,392 +SQL,scatter,0.00597071647644043,392 +SQL,scatter,0.006029605865478516,392 +SQL,scatter,0.00399470329284668,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.004029750823974609,392 +SQL,scatter,0.003998756408691406,392 +SQL,scatter,0.0030248165130615234,392 +SQL,scatter,0.003997087478637695,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.0030007362365722656,392 +SQL,scatter,0.0029997825622558594,392 +SQL,scatter,0.004029273986816406,392 +SQL,scatter,0.004000425338745117,392 +SQL,scatter,0.003969669342041016,392 +SQL,scatter,0.00402522087097168,392 +SQL,scatter,0.003999948501586914,392 +SQL,scatter,0.003974199295043945,392 +SQL,scatter,0.004000186920166016,392 +SQL,scatter,0.004007577896118164,392 +SQL,scatter,0.003999471664428711,392 +SQL,scatter,0.0050008296966552734,392 +SQL,binning,0.0050275325775146484,392 +SQL,binning,0.004002094268798828,392 +SQL,scatter,0.00403141975402832,392 +SQL,get_attributes,0.02300548553466797, +SQL,cardinality,0.0030014514923095703, +SQL,cardinality,0.0010366439819335938, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010151863098144531, +SQL,cardinality,0.0009877681732177734, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0009996891021728516, +SQL,data_type,0.005999326705932617, +SQL,data_type,0.003030061721801758, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0029697418212890625, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003999233245849609, SQL,data_type,0.003999471664428711, -SQL,data_type,0.003956794738769531, -SQL,data_type,0.005998849868774414, -SQL,data_type,0.004030466079711914, -SQL,data_type,0.003972053527832031, -SQL,data_type,0.00302886962890625, -SQL,data_type,0.003992557525634766, -SQL,data_type,0.00400090217590332, +SQL,data_type,0.003970146179199219, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0030024051666259766,392 +SQL,unique_values,0.0010352134704589844,392 +SQL,unique_values,0.0009646415710449219,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0010068416595458984,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009889602661132812,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0009860992431640625,392 +SQL,min_max,0.0009686946868896484,392 +SQL,min_max,0.0029985904693603516,392 +SQL,min_max,0.001971721649169922,392 +SQL,data_length,0.0020041465759277344,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0020227432250976562,392 +SQL,unique_values,0.0019805431365966797,392 +SQL,unique_values,0.0009958744049072266,392 +SQL,unique_values,0.0010128021240234375,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0020112991333007812,392 +SQL,get_attributes,0.003999471664428711, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0020024776458740234, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009965896606445312, +SQL,data_type,0.002997875213623047, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0029990673065185547, SQL,data_type,0.004000186920166016, -SQL,data_length,0.0, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.0040187835693359375, +SQL,data_length,0.0010647773742675781, +SQL,unique_values,0.0010318756103515625,392 +SQL,unique_values,0.0009837150573730469,392 +SQL,unique_values,0.0010309219360351562,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0020127296447753906,392 +SQL,unique_values,0.000980377197265625,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.002007722854614258,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0009753704071044922,392 +SQL,min_max,0.001997709274291992,392 +SQL,scatter,0.0049648284912109375,392 +SQL,scatter,0.0061473846435546875,392 +SQL,scatter,0.005029201507568359,392 +SQL,scatter,0.005028247833251953,392 +SQL,scatter,0.0049974918365478516,392 +SQL,scatter,0.003998994827270508,392 +SQL,scatter,0.003999471664428711,392 +SQL,scatter,0.004998683929443359,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.004027843475341797,392 +SQL,scatter,0.00400090217590332,392 +SQL,scatter,0.004971981048583984,392 +SQL,scatter,0.004971504211425781,392 +SQL,scatter,0.005971670150756836,392 +SQL,scatter,0.005029201507568359,392 +SQL,scatter,0.005968809127807617,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.0039997100830078125,392 +SQL,scatter,0.005026340484619141,392 +SQL,scatter,0.00599980354309082,392 +SQL,binning,0.003000974655151367,392 +SQL,binning,0.0029993057250976562,392 +SQL,binning,0.003000020980834961,392 +SQL,binning,0.0030002593994140625,392 +SQL,binning,0.0029740333557128906,392 +SQL,bar/line,0.008972644805908203,392 +SQL,bar/line,0.009029865264892578,392 +SQL,bar/line,0.0069980621337890625,392 +SQL,bar/line,0.006000518798828125,392 +SQL,scatter,0.005995988845825195,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.004972696304321289,392 +SQL,scatter,0.0069751739501953125,392 +SQL,scatter,0.005026578903198242,392 +SQL,scatter,0.006000041961669922,392 +SQL,scatter,0.0049822330474853516,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.004000663757324219,392 +SQL,scatter,0.003971099853515625,392 +SQL,scatter,0.004027605056762695,392 +SQL,scatter,0.003001689910888672,392 +SQL,scatter,0.004010438919067383,392 +SQL,scatter,0.003974437713623047,392 +SQL,scatter,0.004996538162231445,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.003997802734375,392 +SQL,scatter,0.003027200698852539,392 +SQL,scatter,0.003990888595581055,392 +SQL,scatter,0.0049762725830078125,392 +SQL,scatter,0.00499725341796875,392 +SQL,scatter,0.004998445510864258,392 +SQL,scatter,0.003982067108154297,392 +SQL,scatter,0.004034519195556641,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.003999233245849609,392 +SQL,scatter,0.004969358444213867,392 +SQL,binning,0.0030002593994140625,392 +SQL,binning,0.002999544143676758,392 +SQL,scatter,0.004986286163330078,392 +SQL,get_attributes,0.021992921829223633, +SQL,cardinality,0.003001689910888672, +SQL,cardinality,0.0009698867797851562, +SQL,cardinality,0.0009849071502685547, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0010275840759277344, +SQL,cardinality,0.0009899139404296875, +SQL,cardinality,0.0009658336639404297, +SQL,cardinality,0.0009658336639404297, +SQL,cardinality,0.001999378204345703, +SQL,data_type,0.004006147384643555, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003021240234375, +SQL,data_type,0.00296783447265625, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.005027055740356445, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.002977132797241211, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.0019752979278564453,392 +SQL,unique_values,0.001995086669921875,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.0019927024841308594,392 +SQL,unique_values,0.0009646415710449219,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0020308494567871094,392 +SQL,unique_values,0.0009620189666748047,392 +SQL,unique_values,0.0009655952453613281,392 +SQL,min_max,0.0010042190551757812,392 +SQL,min_max,0.0009753704071044922,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.001027822494506836,392 +SQL,min_max,0.0010304450988769531,392 +SQL,data_length,0.0009706020355224609,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.001964092254638672,392 SQL,unique_values,0.0019991397857666016,392 -SQL,unique_values,0.007996797561645508,392 -SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010275840759277344,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.0019719600677490234,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0019989013671875,392 +SQL,min_max,0.0009713172912597656,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010061264038085938,392 +SQL,min_max,0.001035928726196289,392 +SQL,get_attributes,0.005034685134887695, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0009729862213134766, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0, +SQL,cardinality,0.0009684562683105469, +SQL,cardinality,0.0009953975677490234, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0009744167327880859, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0039827823638916016, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.004012346267700195, +SQL,data_type,0.003031015396118164, +SQL,data_type,0.0039691925048828125, +SQL,data_type,0.005003690719604492, +SQL,data_type,0.005002021789550781, +SQL,data_type,0.003988981246948242, +SQL,data_length,0.0, +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010287761688232422,392 +SQL,unique_values,0.0009696483612060547,392 +SQL,unique_values,0.001054525375366211,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009868144989013672,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010271072387695312,392 +SQL,min_max,0.0009751319885253906,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009691715240478516,392 +SQL,min_max,0.000978708267211914,392 +SQL,binning,0.006000041961669922,392 +SQL,binning,0.00400090217590332,392 +SQL,binning,0.0060002803802490234,392 +SQL,get_attributes,0.022025585174560547, +SQL,cardinality,0.0030670166015625, +SQL,cardinality,0.0009613037109375, +SQL,cardinality,0.0009593963623046875, +SQL,cardinality,0.002003908157348633, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0010027885437011719, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.0020003318786621094, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.004025936126708984, +SQL,data_type,0.0029959678649902344, +SQL,data_type,0.0029692649841308594, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003972291946411133, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.00400090217590332, +SQL,data_length,0.001964092254638672, +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.001971006393432617,392 +SQL,unique_values,0.0010385513305664062,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0020067691802978516,392 +SQL,min_max,0.0010001659393310547,392 +SQL,data_length,0.0020339488983154297,392 +SQL,unique_values,0.002001523971557617,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0009946823120117188,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010292530059814453,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.001971721649169922,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.0010292530059814453,392 +SQL,min_max,0.0009617805480957031,392 +SQL,min_max,0.0009989738464355469,392 +SQL,get_attributes,0.004996776580810547, +SQL,cardinality,0.0019943714141845703, +SQL,cardinality,0.0009791851043701172, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.0010139942169189453, +SQL,cardinality,0.0, +SQL,cardinality,0.0010330677032470703, +SQL,cardinality,0.0009644031524658203, +SQL,cardinality,0.0010018348693847656, +SQL,data_type,0.004004478454589844, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.0040280818939208984, +SQL,data_type,0.002971172332763672, +SQL,data_type,0.0030295848846435547, +SQL,data_type,0.0029723644256591797, +SQL,data_type,0.0029723644256591797, +SQL,data_type,0.003000020980834961, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010251998901367188,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.001020193099975586,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.001972675323486328,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.001001596450805664,392 +SQL,binning,0.006001472473144531,392 +SQL,binning,0.005013704299926758,392 +SQL,binning,0.005999326705932617,392 +SQL,get_attributes,0.022030353546142578, +SQL,cardinality,0.0029997825622558594, +SQL,cardinality,0.0010089874267578125, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0020301342010498047, +SQL,cardinality,0.0009694099426269531, +SQL,cardinality,0.0009620189666748047, +SQL,cardinality,0.0009691715240478516, +SQL,cardinality,0.0, +SQL,cardinality,0.0009708404541015625, +SQL,data_type,0.0029478073120117188, +SQL,data_type,0.005986928939819336, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0030012130737304688, +SQL,data_type,0.0030057430267333984, +SQL,data_type,0.002997159957885742, +SQL,data_type,0.00299835205078125, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010056495666503906,392 +SQL,unique_values,0.001007080078125,392 SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0009634494781494141,392 +SQL,unique_values,0.0010247230529785156,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.0009694099426269531,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0009713172912597656,392 +SQL,min_max,0.001999378204345703,392 +SQL,data_length,0.0009999275207519531,392 +SQL,unique_values,0.0010280609130859375,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010030269622802734,392 +SQL,unique_values,0.001009225845336914,392 +SQL,unique_values,0.0020029544830322266,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010044574737548828,392 +SQL,min_max,0.0019903182983398438,392 +SQL,min_max,0.00101470947265625,392 +SQL,min_max,0.0010273456573486328,392 +SQL,min_max,0.0010290145874023438,392 +SQL,min_max,0.001001596450805664,392 +SQL,get_attributes,0.0040264129638671875, +SQL,cardinality,0.0020287036895751953, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010209083557128906, +SQL,cardinality,0.0009737014770507812, +SQL,cardinality,0.000972747802734375, +SQL,data_type,0.0029754638671875, +SQL,data_type,0.003010272979736328, +SQL,data_type,0.003976583480834961, +SQL,data_type,0.0040056705474853516, +SQL,data_type,0.0029795169830322266, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.004030704498291016, +SQL,data_type,0.005999326705932617, +SQL,data_length,0.002002716064453125, +SQL,unique_values,0.001972198486328125,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0010004043579101562,392 SQL,unique_values,0.0020003318786621094,392 -SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0009720325469970703,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.001987934112548828,392 +SQL,binning,0.006000995635986328,392 +SQL,binning,0.00497126579284668,392 +SQL,binning,0.005028247833251953,392 +SQL,get_attributes,0.02299809455871582, +SQL,cardinality,0.002999544143676758, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0019714832305908203, +SQL,cardinality,0.0020444393157958984, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010380744934082031, +SQL,cardinality,0.002007007598876953, +SQL,cardinality,0.0009603500366210938, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.00399017333984375, +SQL,data_type,0.002977132797241211, +SQL,data_type,0.0030269622802734375, +SQL,data_type,0.00302886962890625, +SQL,data_type,0.003974199295043945, +SQL,data_length,0.0009961128234863281, SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009663105010986328,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0009632110595703125,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009644031524658203,392 SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010273456573486328,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010037422180175781,392 +SQL,min_max,0.0019981861114501953,392 +SQL,min_max,0.001027822494506836,392 +SQL,data_length,0.0009691715240478516,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010035037994384766,392 +SQL,unique_values,0.002032756805419922,392 SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.003029346466064453,392 +SQL,unique_values,0.001974344253540039,392 +SQL,unique_values,0.0010037422180175781,392 +SQL,unique_values,0.002005338668823242,392 +SQL,min_max,0.002000570297241211,392 +SQL,min_max,0.0019974708557128906,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.001997709274291992,392 +SQL,min_max,0.0019698143005371094,392 +SQL,get_attributes,0.006971120834350586, +SQL,cardinality,0.0019986629486083984, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009963512420654297, +SQL,cardinality,0.001035928726196289, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0009696483612060547, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.002000093460083008, +SQL,data_type,0.002970457077026367, +SQL,data_type,0.004028797149658203, +SQL,data_type,0.00600123405456543, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0029687881469726562, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.00403141975402832, +SQL,data_type,0.005001068115234375, +SQL,data_type,0.003970146179199219, +SQL,data_length,0.0009989738464355469, +SQL,unique_values,0.001974344253540039,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010266304016113281,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010306835174560547,392 +SQL,min_max,0.0010101795196533203,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0010344982147216797,392 +SQL,min_max,0.0010023117065429688,392 +SQL,min_max,0.0009875297546386719,392 +SQL,bar/line,0.005999088287353516,392 +SQL,get_attributes,0.02700328826904297, +SQL,cardinality,0.0030012130737304688, +SQL,cardinality,0.002014636993408203, +SQL,cardinality,0.0009963512420654297, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.002006053924560547, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.003024578094482422, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.003972291946411133, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0030274391174316406, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004030942916870117, +SQL,data_length,0.001005411148071289, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0020258426666259766,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.0009717941284179688,392 +SQL,min_max,0.000995635986328125,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009999275207519531,392 +SQL,data_length,0.001026153564453125,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0019714832305908203,392 +SQL,unique_values,0.0010290145874023438,392 +SQL,unique_values,0.001998424530029297,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010230541229248047,392 +SQL,unique_values,0.0010223388671875,392 +SQL,min_max,0.0020284652709960938,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0009818077087402344,392 +SQL,min_max,0.000997304916381836,392 +SQL,get_attributes,0.004975318908691406, +SQL,cardinality,0.0020284652709960938, +SQL,cardinality,0.0010273456573486328, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.001004934310913086, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.001972675323486328, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010383129119873047, +SQL,cardinality,0.0010001659393310547, +SQL,data_type,0.005000591278076172, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.003000020980834961, +SQL,data_type,0.0040013790130615234, +SQL,data_type,0.0049974918365478516, +SQL,data_type,0.004021406173706055, +SQL,data_type,0.0059659481048583984, +SQL,data_type,0.004003286361694336, +SQL,data_type,0.0030260086059570312, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0009691715240478516,392 +SQL,unique_values,0.0020270347595214844,392 +SQL,unique_values,0.0009691715240478516,392 +SQL,unique_values,0.0009644031524658203,392 +SQL,unique_values,0.0009658336639404297,392 +SQL,unique_values,0.001026153564453125,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010304450988769531,392 +SQL,unique_values,0.0009722709655761719,392 +SQL,min_max,0.002034425735473633,392 +SQL,min_max,0.0009684562683105469,392 +SQL,min_max,0.0010306835174560547,392 +SQL,min_max,0.0010302066802978516,392 +SQL,min_max,0.0009753704071044922,392 +SQL,bar/line,0.007002353668212891,392 +SQL,get_attributes,0.02899956703186035, +SQL,cardinality,0.004004001617431641, +SQL,cardinality,0.002972126007080078, +SQL,cardinality,0.0009984970092773438, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.001004934310913086, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.0019974708557128906, +SQL,cardinality,0.002010822296142578, +SQL,data_type,0.0040094852447509766, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.005000114440917969, +SQL,data_type,0.005001544952392578, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.00500178337097168, +SQL,data_type,0.003998756408691406, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.0020089149475097656,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.002001047134399414,392 +SQL,min_max,0.001987934112548828,392 +SQL,min_max,0.002002239227294922,392 SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0010020732879638672,392 +SQL,data_length,0.0010020732879638672,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010051727294921875,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0019958019256591797,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.0019974708557128906,392 +SQL,min_max,0.0019981861114501953,392 +SQL,get_attributes,0.004012346267700195, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.00200653076171875, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010039806365966797, +SQL,cardinality,0.0010035037994384766, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.002000093460083008, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.005013227462768555, +SQL,data_type,0.005002260208129883, +SQL,data_type,0.005997180938720703, +SQL,data_type,0.005002737045288086, +SQL,data_type,0.00500035285949707, +SQL,data_type,0.0050008296966552734, +SQL,data_type,0.006000041961669922, +SQL,data_type,0.005001068115234375, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0010097026824951172,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009884834289550781,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010027885437011719,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.0009982585906982422,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0020008087158203125,392 +SQL,scatter,0.004996538162231445,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.0049974918365478516,392 +SQL,get_attributes,0.021997690200805664, +SQL,cardinality,0.0029985904693603516, +SQL,cardinality,0.0010044574737548828, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.002002239227294922, +SQL,cardinality,0.001001596450805664, +SQL,data_type,0.0029947757720947266, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0030007362365722656, +SQL,data_type,0.003000497817993164, +SQL,data_type,0.0029985904693603516, +SQL,data_type,0.0030145645141601562, +SQL,data_type,0.0029993057250976562, +SQL,data_length,0.0009975433349609375, +SQL,unique_values,0.0010063648223876953,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009970664978027344,392 +SQL,unique_values,0.0010230541229248047,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0010302066802978516,392 +SQL,unique_values,0.00099945068359375,392 +SQL,min_max,0.0010042190551757812,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0020024776458740234,392 +SQL,min_max,0.0010013580322265625,392 +SQL,data_length,0.0009989738464355469,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010306835174560547,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,min_max,0.0010237693786621094,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0020062923431396484,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0009996891021728516,392 +SQL,get_attributes,0.005025625228881836, +SQL,cardinality,0.0020291805267333984, +SQL,cardinality,0.0009911060333251953, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010042190551757812, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004040241241455078, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003960609436035156, +SQL,data_type,0.0030059814453125, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0060312747955322266, +SQL,data_type,0.002990245819091797, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.003974437713623047, +SQL,data_length,0.0010020732879638672, +SQL,unique_values,0.002028226852416992,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010309219360351562,392 +SQL,unique_values,0.0009737014770507812,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0019998550415039062,392 +SQL,binning,0.004002571105957031,392 +SQL,binning,0.003000497817993164,392 +SQL,get_attributes,0.024991273880004883, +SQL,cardinality,0.003000020980834961, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.0020096302032470703, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0009822845458984375, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0009832382202148438, +SQL,cardinality,0.0010027885437011719, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004004240036010742, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.003997087478637695, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.0040051937103271484, +SQL,data_type,0.003998756408691406, +SQL,data_length,0.0010073184967041016, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,unique_values,0.0010104179382324219,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0009729862213134766,392 +SQL,min_max,0.0010294914245605469,392 +SQL,min_max,0.0009703636169433594,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010020732879638672,392 +SQL,data_length,0.0010018348693847656,392 +SQL,unique_values,0.001995563507080078,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,min_max,0.0010156631469726562,392 +SQL,min_max,0.0019714832305908203,392 +SQL,min_max,0.0010051727294921875,392 +SQL,min_max,0.0009739398956298828,392 +SQL,min_max,0.0009763240814208984,392 +SQL,get_attributes,0.0040283203125, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.0010483264923095703, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009634494781494141, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009999275207519531, +SQL,data_type,0.0029900074005126953, +SQL,data_type,0.003973960876464844, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.0030002593994140625, +SQL,data_type,0.002990245819091797, +SQL,data_type,0.0039942264556884766, +SQL,data_type,0.005000591278076172, +SQL,data_type,0.003028392791748047, +SQL,data_type,0.004010915756225586, +SQL,data_length,0.0, +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.000993967056274414,392 +SQL,unique_values,0.0019783973693847656,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0019986629486083984,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0020189285278320312,392 +SQL,min_max,0.0019986629486083984,392 +SQL,bar/line,0.008001565933227539,392 +SQL,bar/line,0.005000114440917969,392 +SQL,bar/line,0.004999637603759766,392 +SQL,get_attributes,0.031009912490844727, +SQL,cardinality,0.005015373229980469, +SQL,cardinality,0.0020029544830322266, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010080337524414062, +SQL,cardinality,0.002002716064453125, +SQL,cardinality,0.0009975433349609375, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.0020012855529785156, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003000020980834961, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010089874267578125,392 +SQL,unique_values,0.001031637191772461,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.00102996826171875,392 +SQL,unique_values,0.0009698867797851562,392 +SQL,min_max,0.0009737014770507812,392 +SQL,min_max,0.0009987354278564453,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.0010356903076171875,392 +SQL,min_max,0.00099945068359375,392 +SQL,data_length,0.0009992122650146484,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.0020377635955810547,392 +SQL,unique_values,0.0010275840759277344,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.001020193099975586,392 +SQL,unique_values,0.001027822494506836,392 +SQL,unique_values,0.0010280609130859375,392 +SQL,unique_values,0.0010349750518798828,392 +SQL,unique_values,0.0009770393371582031,392 +SQL,min_max,0.0009646415710449219,392 +SQL,min_max,0.000988006591796875,392 +SQL,min_max,0.0019903182983398438,392 +SQL,min_max,0.0009708404541015625,392 +SQL,min_max,0.0009648799896240234,392 +SQL,get_attributes,0.004004240036010742, +SQL,cardinality,0.0019974708557128906, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010483264923095703, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.000972747802734375, +SQL,cardinality,0.0010097026824951172, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003964424133300781, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.00299835205078125, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0039980411529541016, +SQL,data_type,0.004000663757324219, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002034425735473633,392 +SQL,unique_values,0.0009660720825195312,392 +SQL,unique_values,0.0020051002502441406,392 +SQL,unique_values,0.001972675323486328,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0019724369049072266,392 +SQL,unique_values,0.0010273456573486328,392 +SQL,unique_values,0.002001523971557617,392 +SQL,min_max,0.001018524169921875,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0009746551513671875,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.00099945068359375,392 +SQL,bar/line,0.009011507034301758,392 +SQL,get_attributes,0.021997451782226562, +SQL,cardinality,0.0019714832305908203, +SQL,cardinality,0.0010259151458740234, +SQL,cardinality,0.001974344253540039, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009686946868896484, +SQL,cardinality,0.0010187625885009766, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.001001119613647461, +SQL,data_type,0.0040302276611328125, +SQL,data_type,0.003974437713623047, +SQL,data_type,0.002993345260620117, +SQL,data_type,0.0029687881469726562, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.0039708614349365234, +SQL,data_type,0.0030298233032226562, +SQL,data_type,0.003997802734375, +SQL,data_type,0.004001140594482422, +SQL,data_length,0.0009722709655761719, +SQL,unique_values,0.001972675323486328,392 +SQL,unique_values,0.0020265579223632812,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009648799896240234,392 +SQL,unique_values,0.0009675025939941406,392 +SQL,unique_values,0.0009741783142089844,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001972198486328125,392 SQL,min_max,0.001035928726196289,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.002027750015258789,392 +SQL,min_max,0.000982522964477539,392 +SQL,data_length,0.0019998550415039062,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0010249614715576172,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001028299331665039,392 +SQL,unique_values,0.0009739398956298828,392 +SQL,unique_values,0.0019812583923339844,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010309219360351562,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,min_max,0.0010230541229248047,392 +SQL,min_max,0.0010254383087158203,392 +SQL,min_max,0.0009710788726806641,392 +SQL,min_max,0.000972747802734375,392 +SQL,min_max,0.0009999275207519531,392 +SQL,get_attributes,0.003991127014160156, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009829998016357422, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0010051727294921875, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009846687316894531, +SQL,cardinality,0.0009965896606445312, +SQL,data_type,0.003003358840942383, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.006025791168212891, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.004007816314697266, +SQL,data_type,0.004965305328369141, +SQL,data_type,0.0029709339141845703, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0029828548431396484, +SQL,data_length,0.00102996826171875, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010318756103515625,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.001024007797241211,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010230541229248047,392 +SQL,min_max,0.001972198486328125,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0009696483612060547,392 +SQL,min_max,0.0019969940185546875,392 +SQL,scatter,0.0050275325775146484,392 +SQL,scatter,0.004971504211425781,392 +SQL,get_attributes,0.02199840545654297, +SQL,cardinality,0.0029990673065185547, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0020003318786621094, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010306835174560547, +SQL,cardinality,0.0, +SQL,cardinality,0.0009975433349609375, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.003980398178100586, +SQL,data_type,0.004004716873168945, +SQL,data_type,0.003968000411987305, +SQL,data_type,0.002995014190673828, +SQL,data_type,0.0040283203125, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004001140594482422, +SQL,data_length,0.0009720325469970703, +SQL,unique_values,0.0010077953338623047,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.0010266304016113281,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001001596450805664,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0019729137420654297,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.00099945068359375,392 +SQL,data_length,0.0009729862213134766,392 +SQL,unique_values,0.0019922256469726562,392 +SQL,unique_values,0.0009737014770507812,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0009961128234863281,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,min_max,0.000993967056274414,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0010037422180175781,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.001008749008178711,392 +SQL,get_attributes,0.003970146179199219, +SQL,cardinality,0.0019989013671875, +SQL,cardinality,0.0009696483612060547, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0009837150573730469, +SQL,cardinality,0.002028942108154297, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009748935699462891, +SQL,cardinality,0.0010263919830322266, +SQL,data_type,0.0039708614349365234, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.00496983528137207, +SQL,data_type,0.0030307769775390625, +SQL,data_type,0.0030171871185302734, +SQL,data_type,0.002968311309814453, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.001971006393432617,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009942054748535156,392 +SQL,unique_values,0.0009732246398925781,392 +SQL,unique_values,0.0010254383087158203,392 +SQL,min_max,0.0020003318786621094,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.002010822296142578,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.001001596450805664,392 +SQL,scatter,0.00496983528137207,392 +SQL,scatter,0.005001068115234375,392 +SQL,scatter,0.004999637603759766,392 +SQL,scatter,0.005004405975341797,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.0270082950592041,392 +SQL,scatter,0.004973649978637695,392 +SQL,scatter,0.00500035285949707,392 +SQL,get_attributes,0.021027326583862305, +SQL,cardinality,0.00299835205078125, +SQL,cardinality,0.0009698867797851562, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.000997304916381836, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.0009706020355224609, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003991603851318359, +SQL,data_type,0.003972053527832031, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.0030024051666259766, +SQL,data_type,0.002969980239868164, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003985881805419922, +SQL,data_type,0.004035234451293945, +SQL,data_length,0.00099945068359375, +SQL,unique_values,0.0020110607147216797,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,unique_values,0.001971721649169922,392 +SQL,min_max,0.0010302066802978516,392 +SQL,min_max,0.001970052719116211,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010004043579101562,392 SQL,min_max,0.0010006427764892578,392 -SQL,min_max,0.0009725093841552734,392 +SQL,data_length,0.0010004043579101562,392 +SQL,unique_values,0.0009808540344238281,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0020012855529785156,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010313987731933594,392 +SQL,min_max,0.002028942108154297,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010151863098144531,392 +SQL,get_attributes,0.005001068115234375, +SQL,cardinality,0.002027750015258789, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0009772777557373047, +SQL,cardinality,0.0009737014770507812, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0010182857513427734, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009710788726806641, +SQL,data_type,0.004979372024536133, +SQL,data_type,0.004024982452392578, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0040283203125, +SQL,data_type,0.005998849868774414, +SQL,data_type,0.0050008296966552734, +SQL,data_type,0.0029706954956054688, +SQL,data_type,0.0039904117584228516, +SQL,data_type,0.003998517990112305, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009908676147460938,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010066032409667969,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.001974821090698242,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.00098419189453125,392 +SQL,min_max,0.0010263919830322266,392 +SQL,min_max,0.0009949207305908203,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009996891021728516,392 +SQL,scatter,0.008001327514648438,392 +SQL,scatter,0.0060274600982666016,392 +SQL,scatter,0.005970001220703125,392 +SQL,scatter,0.005030155181884766,392 +SQL,scatter,0.005971670150756836,392 +SQL,scatter,0.004999876022338867,392 +SQL,scatter,0.0049741268157958984,392 +SQL,scatter,0.006026506423950195,392 +SQL,scatter,0.005971431732177734,392 +SQL,scatter,0.004972934722900391,392 +SQL,scatter,0.004002094268798828,392 +SQL,scatter,0.00597381591796875,392 +SQL,scatter,0.00696873664855957,392 +SQL,scatter,0.005006313323974609,392 +SQL,scatter,0.005000114440917969,392 +SQL,scatter,0.005006313323974609,392 +SQL,scatter,0.004980802536010742,392 +SQL,scatter,0.004971504211425781,392 +SQL,scatter,0.00497126579284668,392 +SQL,scatter,0.006002187728881836,392 +SQL,binning,0.004970550537109375,392 +SQL,binning,0.00400090217590332,392 +SQL,binning,0.0030012130737304688,392 +SQL,binning,0.004000663757324219,392 +SQL,binning,0.005002498626708984,392 +SQL,binning,0.0030286312103271484,392 +SQL,binning,0.0030279159545898438,392 +SQL,binning,0.002971649169921875,392 +SQL,binning,0.002997875213623047,392 +SQL,binning,0.0029997825622558594,392 +SQL,bar/line,0.00999903678894043,392 +SQL,bar/line,0.011000871658325195,392 +SQL,bar/line,0.007990837097167969,392 +SQL,bar/line,0.006971120834350586,392 +SQL,bar/line,0.005999565124511719,392 +SQL,bar/line,0.006972312927246094,392 +SQL,get_attributes,0.02399921417236328, +SQL,cardinality,0.0020303726196289062, +SQL,cardinality,0.0010302066802978516, +SQL,cardinality,0.0020012855529785156, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010051727294921875, +SQL,cardinality,0.0020287036895751953, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010020732879638672, +SQL,data_type,0.005027294158935547, +SQL,data_type,0.003968238830566406, +SQL,data_type,0.003004789352416992, +SQL,data_type,0.0060274600982666016, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.004029273986816406, +SQL,data_type,0.0029993057250976562, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0029706954956054688, +SQL,data_length,0.0010023117065429688, +SQL,unique_values,0.0010297298431396484,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010263919830322266,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0020294189453125,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.001973390579223633,392 +SQL,min_max,0.0009856224060058594,392 +SQL,min_max,0.0020258426666259766,392 +SQL,min_max,0.0009737014770507812,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010280609130859375,392 +SQL,scatter,0.003974199295043945,392 +SQL,get_attributes,0.021012306213378906, +SQL,cardinality,0.0029959678649902344, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009717941284179688, +SQL,cardinality,0.0009598731994628906, +SQL,cardinality,0.0009708404541015625, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0020112991333007812, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010004043579101562, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.003998756408691406, +SQL,data_type,0.0030252933502197266, +SQL,data_type,0.0039882659912109375, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004027366638183594, +SQL,data_type,0.003027200698852539, +SQL,data_type,0.0029883384704589844, +SQL,data_type,0.002973794937133789, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009984970092773438,392 +SQL,unique_values,0.000982522964477539,392 +SQL,unique_values,0.0020301342010498047,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0010292530059814453,392 +SQL,min_max,0.0010113716125488281,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0019974708557128906,392 +SQL,data_length,0.0010001659393310547,392 +SQL,unique_values,0.0010304450988769531,392 +SQL,unique_values,0.0009722709655761719,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0009777545928955078,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010001659393310547,392 SQL,min_max,0.0010006427764892578,392 -SQL,bar/line,0.06202960014343262,392 -SQL,scatter,0.016005992889404297,392 -SQL,scatter,0.0060007572174072266,392 -SQL,bar/line,0.025000572204589844,392 -SQL,bar/line,0.014998674392700195,392 -SQL,scatter,0.01799941062927246,392 -SQL,scatter,0.005998373031616211,392 -SQL,bar/line,0.0049974918365478516,392 -SQL,bar/line,0.009031057357788086,392 -SQL,bar/line,0.00500035285949707,392 -SQL,bar/line,0.009000062942504883,392 -SQL,bar/line,0.008969545364379883,392 -SQL,bar/line,0.00500035285949707,392 -SQL,bar/line,0.005001544952392578,392 -SQL,bar/line,0.00500035285949707,392 -SQL,bar/line,0.006000041961669922,392 -SQL,bar/line,0.004997968673706055,392 -SQL,bar/line,0.004985809326171875,392 -SQL,bar/line,0.007005214691162109,392 -SQL,bar/line,0.006029605865478516,392 -SQL,bar/line,0.00496363639831543,392 -SQL,bar/line,0.0050351619720458984,392 -SQL,bar/line,0.004996538162231445,392 -SQL,bar/line,0.00495147705078125,392 -SQL,bar/line,0.003999948501586914,392 -SQL,bar/line,0.00499725341796875,392 -SQL,bar/line,0.00599980354309082,392 -SQL,bar/line,0.003999948501586914,392 -SQL,bar/line,0.0049991607666015625,392 -SQL,bar/line,0.004999876022338867,392 -SQL,bar/line,0.004998207092285156,392 -SQL,bar/line,0.005998849868774414,392 -SQL,bar/line,0.005000114440917969,392 -SQL,bar/line,0.005982875823974609,392 +SQL,min_max,0.00102996826171875,392 +SQL,min_max,0.0009701251983642578,392 +SQL,min_max,0.0010020732879638672,392 +SQL,min_max,0.0019712448120117188,392 +SQL,get_attributes,0.00403594970703125, +SQL,cardinality,0.0020334720611572266, +SQL,cardinality,0.0009615421295166016, +SQL,cardinality,0.0009703636169433594, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009713172912597656, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.0009987354278564453, +SQL,cardinality,0.0009748935699462891, +SQL,cardinality,0.0010006427764892578, +SQL,data_type,0.002985715866088867, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0040247440338134766, +SQL,data_type,0.003998994827270508, +SQL,data_type,0.004003763198852539, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004006147384643555, +SQL,data_type,0.0039713382720947266, +SQL,data_length,0.0010004043579101562, +SQL,unique_values,0.0010309219360351562,392 +SQL,unique_values,0.0019729137420654297,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000570297241211,392 +SQL,unique_values,0.0019989013671875,392 +SQL,unique_values,0.0009982585906982422,392 +SQL,unique_values,0.0,392 +SQL,unique_values,0.0020055770874023438,392 +SQL,min_max,0.0009617805480957031,392 +SQL,min_max,0.0010037422180175781,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010290145874023438,392 +SQL,min_max,0.0010013580322265625,392 SQL,bar/line,0.004999637603759766,392 -SQL,bar/line,0.004996538162231445,392 -SQL,bar/line,0.0049974918365478516,392 -SQL,bar/line,0.005000591278076172,392 -SQL,bar/line,0.004999399185180664,392 -SQL,bar/line,0.004996776580810547,392 -SQL,bar/line,0.004000186920166016,392 -SQL,bar/line,0.0059969425201416016,392 -SQL,binning,0.003998279571533203,392 SQL,bar/line,0.005000591278076172,392 -SQL,data_length,0.4390373229980469, -SQL,get_attributes,0.022954702377319336, -SQL,cardinality,0.23603153228759766, -SQL,cardinality,0.9599747657775879, -SQL,cardinality,0.30799126625061035, -SQL,cardinality,0.2690298557281494, -SQL,cardinality,1.008005142211914, -SQL,cardinality,1.5690290927886963, -SQL,cardinality,1.6099779605865479, -SQL,cardinality,0.23402714729309082, -SQL,cardinality,0.30198097229003906, -SQL,cardinality,0.3039853572845459, -SQL,cardinality,0.2910025119781494, -SQL,data_type,0.003998756408691406, -SQL,data_type,0.004000186920166016, -SQL,data_type,0.004006385803222656, -SQL,data_type,0.00400543212890625, +SQL,bar/line,0.005029201507568359,392 +SQL,get_attributes,0.020000457763671875, +SQL,cardinality,0.0030002593994140625, +SQL,cardinality,0.0020008087158203125, +SQL,cardinality,0.0019707679748535156, +SQL,cardinality,0.0009710788726806641, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0009703636169433594, +SQL,cardinality,0.0010001659393310547, +SQL,cardinality,0.002003908157348633, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.004030466079711914, +SQL,data_type,0.0029697418212890625, +SQL,data_type,0.004000186920166016, +SQL,data_type,0.002970457077026367, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0039975643157958984, SQL,data_type,0.004001140594482422, -SQL,data_type,0.0039865970611572266, -SQL,data_type,0.0029973983764648438, -SQL,data_type,0.003973484039306641, -SQL,data_type,0.002974271774291992, -SQL,data_type,0.002972841262817383, +SQL,data_type,0.003978252410888672, +SQL,data_length,0.0009999275207519531, +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.0010304450988769531,392 +SQL,unique_values,0.0010094642639160156,392 +SQL,unique_values,0.002008676528930664,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010263919830322266,392 +SQL,unique_values,0.0009872913360595703,392 +SQL,min_max,0.0010213851928710938,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010135173797607422,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0019805431365966797,392 +SQL,data_length,0.0,392 +SQL,unique_values,0.002025127410888672,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0019788742065429688,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0009624958038330078,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009703636169433594,392 +SQL,min_max,0.0009717941284179688,392 +SQL,min_max,0.0009791851043701172,392 +SQL,min_max,0.0009713172912597656,392 +SQL,min_max,0.0010068416595458984,392 +SQL,min_max,0.001003265380859375,392 +SQL,get_attributes,0.005003213882446289, +SQL,cardinality,0.002027750015258789, +SQL,cardinality,0.000972747802734375, +SQL,cardinality,0.0009729862213134766, +SQL,cardinality,0.0009706020355224609, +SQL,cardinality,0.0009629726409912109, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.001016855239868164, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010266304016113281, +SQL,data_type,0.003997802734375, +SQL,data_type,0.0029714107513427734, +SQL,data_type,0.0029990673065185547, +SQL,data_type,0.003973722457885742, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003977537155151367, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.003968000411987305, +SQL,data_length,0.0010008811950683594, +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010263919830322266,392 +SQL,unique_values,0.0009832382202148438,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0020284652709960938,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.0010251998901367188,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.000997304916381836,392 +SQL,bar/line,0.005028963088989258,392 +SQL,bar/line,0.006001949310302734,392 +SQL,bar/line,0.004998922348022461,392 +SQL,get_attributes,0.02102828025817871, +SQL,cardinality,0.0039997100830078125, +SQL,cardinality,0.0010304450988769531, +SQL,cardinality,0.002007722854614258, +SQL,cardinality,0.0010013580322265625, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.001031637191772461, +SQL,cardinality,0.001001119613647461, +SQL,data_type,0.0030264854431152344, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.004029512405395508, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.003002166748046875, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.003973722457885742, +SQL,data_type,0.004026651382446289, +SQL,data_length,0.001001596450805664, +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.001027822494506836,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.002015352249145508,392 +SQL,unique_values,0.0010306835174560547,392 +SQL,unique_values,0.0020093917846679688,392 +SQL,unique_values,0.002005338668823242,392 +SQL,unique_values,0.0009660720825195312,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,min_max,0.002010822296142578,392 +SQL,min_max,0.0010025501251220703,392 +SQL,min_max,0.0009713172912597656,392 +SQL,min_max,0.0010292530059814453,392 +SQL,min_max,0.001955747604370117,392 +SQL,data_length,0.0010192394256591797,392 +SQL,unique_values,0.0009765625,392 +SQL,unique_values,0.0009665489196777344,392 +SQL,unique_values,0.0020334720611572266,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.001039743423461914,392 +SQL,unique_values,0.002002716064453125,392 +SQL,unique_values,0.0010111331939697266,392 +SQL,unique_values,0.0009708404541015625,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,min_max,0.0009696483612060547,392 +SQL,min_max,0.0009791851043701172,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0009737014770507812,392 +SQL,get_attributes,0.0050008296966552734, +SQL,cardinality,0.001997232437133789, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.0010073184967041016, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0, +SQL,cardinality,0.0009737014770507812, +SQL,cardinality,0.0009737014770507812, +SQL,cardinality,0.0, +SQL,cardinality,0.0009734630584716797, +SQL,data_type,0.004030466079711914, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0039713382720947266, +SQL,data_type,0.0040285587310791016, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003978729248046875, +SQL,data_type,0.005001544952392578, +SQL,data_type,0.0030045509338378906, +SQL,data_type,0.003997802734375, +SQL,data_length,0.0009970664978027344, +SQL,unique_values,0.0009927749633789062,392 +SQL,unique_values,0.0009965896606445312,392 +SQL,unique_values,0.0009975433349609375,392 +SQL,unique_values,0.0010256767272949219,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.000978231430053711,392 +SQL,unique_values,0.000997781753540039,392 +SQL,min_max,0.0010018348693847656,392 +SQL,min_max,0.0009703636169433594,392 +SQL,min_max,0.000997304916381836,392 +SQL,min_max,0.0010313987731933594,392 +SQL,min_max,0.0019724369049072266,392 +SQL,bar/line,0.012972593307495117,392 +SQL,get_attributes,0.023025035858154297, +SQL,cardinality,0.002994537353515625, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010082721710205078, +SQL,cardinality,0.0009722709655761719, +SQL,cardinality,0.0009829998016357422, +SQL,cardinality,0.0010256767272949219, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0010101795196533203, +SQL,data_type,0.003998279571533203, +SQL,data_type,0.0069844722747802734, +SQL,data_type,0.0029430389404296875, +SQL,data_type,0.0029740333557128906, +SQL,data_type,0.003972768783569336, +SQL,data_type,0.003028392791748047, +SQL,data_type,0.0050029754638671875, +SQL,data_type,0.003977060317993164, +SQL,data_type,0.002962827682495117, +SQL,data_length,0.001001119613647461, +SQL,unique_values,0.0020380020141601562,392 +SQL,unique_values,0.0010254383087158203,392 +SQL,unique_values,0.0009701251983642578,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0020024776458740234,392 +SQL,unique_values,0.000972747802734375,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0010361671447753906,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0009646415710449219,392 +SQL,min_max,0.0009646415710449219,392 +SQL,data_length,0.0009722709655761719,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.000972747802734375,392 +SQL,unique_values,0.0009722709655761719,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010228157043457031,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,min_max,0.0009698867797851562,392 +SQL,min_max,0.0009722709655761719,392 +SQL,min_max,0.0010306835174560547,392 +SQL,min_max,0.0009989738464355469,392 +SQL,min_max,0.0010008811950683594,392 +SQL,get_attributes,0.0050013065338134766, +SQL,cardinality,0.002000570297241211, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010025501251220703, +SQL,cardinality,0.0009911060333251953, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0019915103912353516, +SQL,cardinality,0.0010294914245605469, +SQL,cardinality,0.001001596450805664, +SQL,data_type,0.004028797149658203, +SQL,data_type,0.0059740543365478516, +SQL,data_type,0.0040130615234375, +SQL,data_type,0.003968477249145508, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0030312538146972656, +SQL,data_type,0.004973411560058594, +SQL,data_type,0.00503087043762207, +SQL,data_length,0.0009908676147460938, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002998828887939453,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.001994609832763672,392 +SQL,unique_values,0.002041339874267578,392 +SQL,unique_values,0.0009622573852539062,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0019707679748535156,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0010271072387695312,392 +SQL,min_max,0.0010311603546142578,392 +SQL,min_max,0.001026153564453125,392 +SQL,bar/line,0.012027978897094727,392 +SQL,get_attributes,0.022029399871826172, +SQL,cardinality,0.003028392791748047, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010273456573486328, +SQL,cardinality,0.001977205276489258, +SQL,cardinality,0.0009999275207519531, +SQL,cardinality,0.0010111331939697266, +SQL,cardinality,0.0009720325469970703, +SQL,cardinality,0.0009970664978027344, +SQL,cardinality,0.0009706020355224609, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.00397038459777832, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.003972530364990234, +SQL,data_type,0.002998828887939453, +SQL,data_type,0.003999471664428711, +SQL,data_length,0.0009980201721191406, +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0009710788726806641,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010254383087158203,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,unique_values,0.0009980201721191406,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.002001523971557617,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0019986629486083984,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.002002716064453125,392 +SQL,data_length,0.001013040542602539,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0019998550415039062,392 +SQL,unique_values,0.001993417739868164,392 +SQL,unique_values,0.000997781753540039,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001986265182495117,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.002000570297241211,392 +SQL,min_max,0.0009868144989013672,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.0020055770874023438,392 +SQL,min_max,0.0019998550415039062,392 +SQL,get_attributes,0.004999637603759766, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.002004861831665039, +SQL,cardinality,0.0010128021240234375, +SQL,cardinality,0.0009980201721191406, +SQL,cardinality,0.0009970664978027344, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.001001119613647461, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.004002094268798828, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0029997825622558594, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004999876022338867, +SQL,data_type,0.003998517990112305, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004000425338745117, +SQL,data_length,0.0009992122650146484, +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010099411010742188,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.001999378204345703,392 +SQL,unique_values,0.0019941329956054688,392 +SQL,unique_values,0.0010237693786621094,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0020017623901367188,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.0010097026824951172,392 +SQL,min_max,0.0020036697387695312,392 +SQL,bar/line,0.005001544952392578,392 +SQL,get_attributes,0.025000810623168945, +SQL,cardinality,0.0029840469360351562, +SQL,cardinality,0.002000093460083008, +SQL,cardinality,0.0020017623901367188, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.001999378204345703, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.0009996891021728516, +SQL,data_type,0.004976511001586914, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004001140594482422, +SQL,data_type,0.0040018558502197266, +SQL,data_type,0.0029981136322021484, +SQL,data_type,0.0039861202239990234, +SQL,data_type,0.002996206283569336, +SQL,data_length,0.0010006427764892578, +SQL,unique_values,0.0020017623901367188,392 +SQL,unique_values,0.0019981861114501953,392 +SQL,unique_values,0.0010018348693847656,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.002003908157348633,392 +SQL,min_max,0.001001596450805664,392 +SQL,data_length,0.0010018348693847656,392 +SQL,unique_values,0.0010027885437011719,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.002002239227294922,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.00099945068359375,392 +SQL,min_max,0.002001523971557617,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0019991397857666016,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.002002239227294922,392 +SQL,get_attributes,0.0060236454010009766, +SQL,cardinality,0.0029990673065185547, +SQL,cardinality,0.0010006427764892578, +SQL,cardinality,0.0010166168212890625, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.002004384994506836, +SQL,cardinality,0.0009987354278564453, +SQL,data_type,0.003993034362792969, +SQL,data_type,0.002999544143676758, +SQL,data_type,0.0030014514923095703, +SQL,data_type,0.003003358840942383, +SQL,data_type,0.002994060516357422, +SQL,data_type,0.005006551742553711, +SQL,data_type,0.004873514175415039, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.004021883010864258, +SQL,data_length,0.0009987354278564453, +SQL,unique_values,0.001941680908203125,392 +SQL,unique_values,0.0019724369049072266,392 +SQL,unique_values,0.0009734630584716797,392 +SQL,unique_values,0.0019719600677490234,392 +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.0019996166229248047,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0020036697387695312,392 +SQL,min_max,0.0010249614715576172,392 +SQL,min_max,0.00099945068359375,392 +SQL,min_max,0.002032041549682617,392 +SQL,binning,0.004967689514160156,392 +SQL,get_attributes,0.0190274715423584, +SQL,cardinality,0.002011537551879883, +SQL,cardinality,0.0009601116180419922, +SQL,cardinality,0.0009582042694091797, +SQL,cardinality,0.0009903907775878906, +SQL,cardinality,0.002028226852416992, +SQL,cardinality,0.0009615421295166016, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0010223388671875, +SQL,cardinality,0.0010008811950683594, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0030341148376464844, +SQL,data_type,0.004004001617431641, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.0050013065338134766, +SQL,data_type,0.003993034362792969, +SQL,data_type,0.004027605056762695, +SQL,data_type,0.002990245819091797, +SQL,data_type,0.004007577896118164, +SQL,data_length,0.0009610652923583984, +SQL,unique_values,0.0009601116180419922,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0009968280792236328,392 +SQL,unique_values,0.0009987354278564453,392 +SQL,unique_values,0.0009989738464355469,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010330677032470703,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0009658336639404297,392 +SQL,min_max,0.0009667873382568359,392 +SQL,min_max,0.0009999275207519531,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.0009944438934326172,392 +SQL,min_max,0.0010058879852294922,392 +SQL,data_length,0.0009722709655761719,392 +SQL,unique_values,0.0009629726409912109,392 +SQL,unique_values,0.0009682178497314453,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009632110595703125,392 +SQL,unique_values,0.0009729862213134766,392 +SQL,unique_values,0.0019991397857666016,392 +SQL,unique_values,0.001024484634399414,392 +SQL,unique_values,0.0009629726409912109,392 +SQL,unique_values,0.001005411148071289,392 +SQL,min_max,0.0009624958038330078,392 +SQL,min_max,0.0009758472442626953,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.0009725093841552734,392 +SQL,min_max,0.0020012855529785156,392 +SQL,get_attributes,0.004023075103759766, +SQL,cardinality,0.0019998550415039062, +SQL,cardinality,0.0009715557098388672, +SQL,cardinality,0.0009794235229492188, +SQL,cardinality,0.0009617805480957031, +SQL,cardinality,0.0009641647338867188, +SQL,cardinality,0.0009620189666748047, +SQL,cardinality,0.001027822494506836, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0009768009185791016, +SQL,data_type,0.0030088424682617188, +SQL,data_type,0.002970457077026367, +SQL,data_type,0.0030059814453125, +SQL,data_type,0.004038333892822266, +SQL,data_type,0.002979278564453125, +SQL,data_type,0.0050008296966552734, +SQL,data_type,0.00503087043762207, +SQL,data_type,0.0039517879486083984, +SQL,data_type,0.004001140594482422, +SQL,data_length,0.0009987354278564453, +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010094642639160156,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0010390281677246094,392 +SQL,unique_values,0.0010256767272949219,392 +SQL,unique_values,0.0009622573852539062,392 +SQL,min_max,0.0010139942169189453,392 +SQL,min_max,0.002026081085205078,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0009984970092773438,392 +SQL,min_max,0.0019714832305908203,392 +SQL,binning,0.0029993057250976562,392 +SQL,get_attributes,0.021999120712280273, +SQL,cardinality,0.0029997825622558594, +SQL,cardinality,0.0009989738464355469, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0020270347595214844, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.001026153564453125, +SQL,cardinality,0.0009627342224121094, +SQL,data_type,0.00400090217590332, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.0039637088775634766, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.005992412567138672, +SQL,data_type,0.0030221939086914062, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004001617431640625, +SQL,data_type,0.003002166748046875, +SQL,data_length,0.001013040542602539, +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010082721710205078,392 +SQL,unique_values,0.0009667873382568359,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001027822494506836,392 +SQL,unique_values,0.0010235309600830078,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.001003265380859375,392 +SQL,unique_values,0.0,392 +SQL,min_max,0.0010249614715576172,392 +SQL,min_max,0.001998424530029297,392 +SQL,min_max,0.002002239227294922,392 +SQL,min_max,0.001001596450805664,392 +SQL,min_max,0.0010008811950683594,392 +SQL,data_length,0.001001596450805664,392 +SQL,unique_values,0.0009887218475341797,392 +SQL,unique_values,0.001974344253540039,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0010373592376708984,392 +SQL,unique_values,0.0020003318786621094,392 +SQL,unique_values,0.001961231231689453,392 +SQL,unique_values,0.0009746551513671875,392 +SQL,unique_values,0.001960277557373047,392 +SQL,min_max,0.0009996891021728516,392 +SQL,min_max,0.001039743423461914,392 +SQL,min_max,0.0010008811950683594,392 +SQL,min_max,0.0010247230529785156,392 +SQL,min_max,0.0009822845458984375,392 +SQL,get_attributes,0.004000425338745117, +SQL,cardinality,0.0020051002502441406, +SQL,cardinality,0.0010066032409667969, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.001016378402709961, +SQL,cardinality,0.0, +SQL,cardinality,0.001008749008178711, +SQL,cardinality,0.0009975433349609375, +SQL,cardinality,0.002058267593383789, +SQL,cardinality,0.0009598731994628906, +SQL,data_type,0.0039882659912109375, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.0039632320404052734, SQL,data_type,0.004000186920166016, -SQL,data_length,0.1019754409790039, -SQL,unique_values,0.18699216842651367,855632 -SQL,unique_values,0.19903802871704102,855632 -SQL,unique_values,0.2119584083557129,855632 -SQL,unique_values,0.2320091724395752,855632 -SQL,unique_values,0.40199899673461914,855632 -SQL,unique_values,0.26400327682495117,855632 -SQL,unique_values,0.2580232620239258,855632 -SQL,unique_values,0.23099470138549805,855632 -SQL,unique_values,0.21199774742126465,855632 -SQL,unique_values,0.22099685668945312,855632 -SQL,unique_values,0.23600482940673828,855632 -SQL,min_max,0.13499760627746582,855632 -SQL,min_max,0.13000059127807617,855632 -SQL,min_max,0.1269996166229248,855632 -SQL,min_max,0.136993408203125,855632 -SQL,scatter,0.5110056400299072,855632 -SQL,bar/line,0.24703669548034668,855632 -SQL,bar/line,0.2299642562866211,855632 -SQL,bar/line,0.24700164794921875,855632 -SQL,bar/line,0.25499773025512695,855632 -SQL,bar/line,0.22700190544128418,855632 -SQL,bar/line,0.22902965545654297,855632 -SQL,bar/line,0.22299933433532715,855632 -SQL,scatter,0.4289736747741699,855632 -SQL,scatter,0.46297359466552734,855632 -SQL,binning,0.16697144508361816,855632 -SQL,binning,0.1860051155090332,855632 -SQL,binning,0.1529994010925293,855632 -SQL,binning,0.1509997844696045,855632 -SQL,binning,0.15399837493896484,855632 -SQL,binning,0.17000150680541992,855632 -SQL,binning,0.24300146102905273,855632 -SQL,binning,0.16000032424926758,855632 -SQL,binning,0.16599798202514648,855632 -SQL,binning,0.15699982643127441,855632 -SQL,binning,0.17099928855895996,855632 -SQL,binning,0.15602993965148926,855632 -SQL,binning,0.17097210884094238,855632 -SQL,binning,0.19099950790405273,855632 -SQL,binning,0.178999662399292,855632 -SQL,binning,0.1770000457763672,855632 -SQL,binning,0.1810007095336914,855632 -SQL,binning,0.1589975357055664,855632 -SQL,binning,0.14600157737731934,855632 -SQL,binning,0.2389979362487793,855632 -SQL,binning,0.1509692668914795,855632 -SQL,binning,0.24400067329406738,855632 -SQL,binning,0.19100356101989746,855632 -SQL,binning,0.18199706077575684,855632 -SQL,binning,0.19204258918762207,855632 -SQL,binning,0.1809558868408203,855632 -SQL,binning,0.1620008945465088,855632 -SQL,binning,0.17702770233154297,855632 -SQL,binning,0.17197012901306152,855632 -SQL,binning,0.17502617835998535,855632 -SQL,binning,0.1770002841949463,855632 -SQL,binning,0.15999364852905273,855632 -SQL,binning,0.1810016632080078,855632 -SQL,binning,0.17600631713867188,855632 -SQL,binning,0.1679975986480713,855632 -SQL,binning,0.18300127983093262,855632 -SQL,binning,0.16299724578857422,855632 -SQL,binning,0.23700404167175293,855632 -SQL,binning,0.17902159690856934,855632 -SQL,binning,0.16402435302734375,855632 -SQL,binning,0.1809694766998291,855632 -SQL,binning,0.18699932098388672,855632 -SQL,binning,0.17499852180480957,855632 -SQL,binning,0.22701239585876465,855632 -SQL,binning,0.2349870204925537,855632 -SQL,binning,0.20799946784973145,855632 -SQL,binning,0.21599698066711426,855632 -SQL,binning,0.20099854469299316,855632 -SQL,binning,0.19900250434875488,855632 -SQL,binning,0.22300028800964355,855632 -SQL,binning,0.19199609756469727,855632 -SQL,binning,0.19299960136413574,855632 -SQL,binning,0.18200325965881348,855632 -SQL,binning,0.17400002479553223,855632 -SQL,binning,0.17800235748291016,855632 -SQL,binning,0.17699813842773438,855632 -SQL,binning,0.19500064849853516,855632 -SQL,binning,0.17497730255126953,855632 -SQL,binning,0.1929621696472168,855632 -SQL,binning,0.17600107192993164,855632 -SQL,binning,0.19400453567504883,855632 -SQL,binning,0.18999528884887695,855632 -SQL,binning,0.17762160301208496,855632 -SQL,binning,0.1789999008178711,855632 -SQL,binning,0.1880016326904297,855632 -SQL,binning,0.17799901962280273,855632 -SQL,binning,0.17299842834472656,855632 -SQL,binning,0.1919994354248047,855632 -SQL,binning,0.18200135231018066,855632 -SQL,binning,0.17800188064575195,855632 -SQL,binning,0.21900367736816406,855632 -SQL,binning,0.17899870872497559,855632 -SQL,binning,0.26000332832336426,855632 -SQL,binning,0.2559993267059326,855632 -SQL,binning,0.24900007247924805,855632 -SQL,binning,0.26599836349487305,855632 -SQL,binning,0.234999418258667,855632 -SQL,binning,0.2360219955444336,855632 -SQL,binning,0.21602606773376465,855632 -SQL,binning,0.23002862930297852,855632 -SQL,binning,0.23699641227722168,855632 -SQL,binning,0.21099066734313965,855632 -SQL,binning,0.2220296859741211,855632 -SQL,binning,0.21299481391906738,855632 -SQL,binning,0.2259986400604248,855632 -SQL,binning,0.22899937629699707,855632 -SQL,binning,0.2869997024536133,855632 -SQL,binning,0.23699665069580078,855632 -SQL,binning,0.25300168991088867,855632 -SQL,binning,0.2189939022064209,855632 -SQL,binning,0.2500288486480713,855632 -SQL,binning,0.21497344970703125,855632 -SQL,binning,0.2259969711303711,855632 -SQL,binning,0.20899105072021484,855632 -SQL,binning,0.23602628707885742,855632 -SQL,binning,0.22900009155273438,855632 -SQL,binning,0.21903157234191895,855632 -SQL,binning,0.2899963855743408,855632 -SQL,binning,0.2609999179840088,855632 -SQL,binning,0.255995512008667,855632 -SQL,binning,0.2579991817474365,855632 -SQL,binning,0.2529940605163574,855632 -SQL,binning,0.23200178146362305,855632 -SQL,binning,0.21999859809875488,855632 -SQL,binning,0.2349991798400879,855632 -SQL,binning,0.2350015640258789,855632 -SQL,binning,0.24399900436401367,855632 -SQL,binning,0.21799921989440918,855632 -SQL,binning,0.2189955711364746,855632 -SQL,binning,0.2340095043182373,855632 -SQL,binning,0.22798752784729004,855632 -SQL,binning,0.2999989986419678,855632 -SQL,binning,0.23201632499694824,855632 -SQL,binning,0.23099613189697266,855632 -SQL,binning,0.2050013542175293,855632 -SQL,binning,0.21700072288513184,855632 -SQL,binning,0.2019963264465332,855632 -SQL,binning,0.22099924087524414,855632 -SQL,binning,0.25800085067749023,855632 -SQL,binning,0.23799753189086914,855632 -SQL,binning,0.2180004119873047,855632 -SQL,binning,0.26999950408935547,855632 -SQL,binning,0.3159980773925781,855632 -SQL,binning,0.2559993267059326,855632 -SQL,binning,0.2340383529663086,855632 -SQL,binning,0.228010892868042,855632 -SQL,binning,0.21796965599060059,855632 -SQL,binning,0.23699736595153809,855632 -SQL,binning,0.1979973316192627,855632 -SQL,binning,0.22599363327026367,855632 -SQL,binning,0.22502636909484863,855632 -SQL,binning,0.22297263145446777,855632 -SQL,binning,0.2259984016418457,855632 -SQL,binning,0.23200297355651855,855632 -SQL,binning,0.21498584747314453,855632 -SQL,binning,0.24000310897827148,855632 -SQL,binning,0.23299837112426758,855632 -SQL,binning,0.22999835014343262,855632 -SQL,binning,0.24399495124816895,855632 -SQL,binning,0.24900031089782715,855632 -SQL,binning,0.23900103569030762,855632 -SQL,binning,0.22200560569763184,855632 -SQL,binning,0.22502446174621582,855632 -SQL,binning,0.21500563621520996,855632 -SQL,binning,0.2150261402130127,855632 -SQL,binning,0.27100443840026855,855632 -SQL,binning,0.28099966049194336,855632 -SQL,binning,0.2700042724609375,855632 -SQL,binning,0.23999619483947754,855632 +SQL,data_type,0.004039287567138672, +SQL,data_type,0.004038810729980469, +SQL,data_type,0.003007173538208008, +SQL,data_type,0.003967761993408203, +SQL,data_type,0.002969980239868164, +SQL,data_length,0.0009970664978027344, +SQL,unique_values,0.001989126205444336,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010075569152832031,392 +SQL,unique_values,0.0009617805480957031,392 +SQL,unique_values,0.0009636878967285156,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010459423065185547,392 +SQL,unique_values,0.0009725093841552734,392 +SQL,unique_values,0.0009655952453613281,392 +SQL,min_max,0.0009815692901611328,392 +SQL,min_max,0.0010251998901367188,392 +SQL,min_max,0.0020012855529785156,392 +SQL,min_max,0.0009706020355224609,392 +SQL,min_max,0.0010251998901367188,392 +SQL,bar/line,0.008985757827758789,392 +SQL,get_attributes,0.02696967124938965, +SQL,cardinality,0.002999544143676758, +SQL,cardinality,0.0019898414611816406, +SQL,cardinality,0.0009701251983642578, +SQL,cardinality,0.0009815692901611328, +SQL,cardinality,0.0010259151458740234, +SQL,cardinality,0.0019757747650146484, +SQL,cardinality,0.0009725093841552734, +SQL,cardinality,0.0010018348693847656, +SQL,cardinality,0.0009598731994628906, +SQL,data_type,0.005004167556762695, +SQL,data_type,0.0030040740966796875, +SQL,data_type,0.003003358840942383, +SQL,data_type,0.003997802734375, +SQL,data_type,0.003954648971557617, +SQL,data_type,0.003013134002685547, +SQL,data_type,0.0030088424682617188, +SQL,data_type,0.002958059310913086, +SQL,data_type,0.0030481815338134766, +SQL,data_length,0.0009202957153320312, +SQL,unique_values,0.0009992122650146484,392 +SQL,unique_values,0.0010061264038085938,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,unique_values,0.0010254383087158203,392 +SQL,unique_values,0.0019979476928710938,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,unique_values,0.0019707679748535156,392 +SQL,unique_values,0.002000570297241211,392 +SQL,min_max,0.0019998550415039062,392 +SQL,min_max,0.002001047134399414,392 +SQL,min_max,0.002004384994506836,392 +SQL,min_max,0.001989126205444336,392 +SQL,min_max,0.0010311603546142578,392 +SQL,data_length,0.0010046958923339844,392 +SQL,unique_values,0.001972198486328125,392 +SQL,unique_values,0.0020008087158203125,392 +SQL,unique_values,0.0010292530059814453,392 +SQL,unique_values,0.0010292530059814453,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0009717941284179688,392 +SQL,unique_values,0.0009720325469970703,392 +SQL,unique_values,0.0009715557098388672,392 +SQL,min_max,0.0010073184967041016,392 +SQL,min_max,0.0009732246398925781,392 +SQL,min_max,0.0009992122650146484,392 +SQL,min_max,0.0010056495666503906,392 +SQL,min_max,0.0019991397857666016,392 +SQL,get_attributes,0.004973649978637695, +SQL,cardinality,0.002023935317993164, +SQL,cardinality,0.0009982585906982422, +SQL,cardinality,0.0010004043579101562, +SQL,cardinality,0.0009732246398925781, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0009608268737792969, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0009996891021728516, +SQL,cardinality,0.0020024776458740234, +SQL,data_type,0.004000425338745117, +SQL,data_type,0.0030024051666259766, +SQL,data_type,0.002996683120727539, +SQL,data_type,0.003971099853515625, +SQL,data_type,0.004030942916870117, +SQL,data_type,0.005989551544189453, +SQL,data_type,0.0029718875885009766, +SQL,data_type,0.003999233245849609, +SQL,data_type,0.004029512405395508, +SQL,data_length,0.0009717941284179688, +SQL,unique_values,0.0010380744934082031,392 +SQL,unique_values,0.0009686946868896484,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.001001596450805664,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.00099945068359375,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.001001119613647461,392 +SQL,unique_values,0.001031637191772461,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.002002716064453125,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.001001119613647461,392 +SQL,min_max,0.001972675323486328,392 +SQL,bar/line,0.01399993896484375,392 +SQL,get_attributes,0.019998788833618164, +SQL,cardinality,0.0030002593994140625, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.001993894577026367, +SQL,cardinality,0.0010287761688232422, +SQL,cardinality,0.00099945068359375, +SQL,cardinality,0.0020020008087158203, +SQL,cardinality,0.0019996166229248047, +SQL,cardinality,0.002000570297241211, +SQL,data_type,0.003999471664428711, +SQL,data_type,0.004026174545288086, +SQL,data_type,0.003987789154052734, +SQL,data_type,0.005005598068237305, +SQL,data_type,0.003999948501586914, +SQL,data_type,0.003985881805419922, +SQL,data_type,0.0039997100830078125, +SQL,data_type,0.0040094852447509766, +SQL,data_type,0.0049591064453125, +SQL,data_length,0.0009913444519042969, +SQL,unique_values,0.0010077953338623047,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0009999275207519531,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0009706020355224609,392 +SQL,unique_values,0.0010023117065429688,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.0010025501251220703,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,min_max,0.0009961128234863281,392 +SQL,min_max,0.002035856246948242,392 +SQL,min_max,0.002000093460083008,392 +SQL,min_max,0.0009644031524658203,392 +SQL,min_max,0.0010008811950683594,392 +SQL,data_length,0.0009942054748535156,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0009996891021728516,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010004043579101562,392 +SQL,unique_values,0.0010008811950683594,392 +SQL,unique_values,0.002001047134399414,392 +SQL,unique_values,0.0010020732879638672,392 +SQL,unique_values,0.0010013580322265625,392 +SQL,unique_values,0.0010006427764892578,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010006427764892578,392 +SQL,min_max,0.0010001659393310547,392 +SQL,min_max,0.0010004043579101562,392 +SQL,min_max,0.0010006427764892578,392 +SQL,get_attributes,0.004000663757324219, +SQL,cardinality,0.002001047134399414, +SQL,cardinality,0.001001596450805664, +SQL,cardinality,0.0009992122650146484, +SQL,cardinality,0.0010020732879638672, +SQL,cardinality,0.001001119613647461, +SQL,cardinality,0.0019991397857666016, +SQL,cardinality,0.0010023117065429688, +SQL,cardinality,0.0010008811950683594, +SQL,cardinality,0.0009980201721191406, +SQL,data_type,0.004000663757324219, +SQL,data_type,0.00403594970703125, +SQL,data_type,0.004039287567138672, +SQL,data_type,0.00396275520324707, +SQL,data_type,0.0030393600463867188, +SQL,data_type,0.003985404968261719, +SQL,data_type,0.0040378570556640625, +SQL,data_type,0.003996849060058594, +SQL,data_type,0.003995180130004883, +SQL,data_length,0.0009613037109375, +SQL,unique_values,0.0010068416595458984,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010399818420410156,392 +SQL,unique_values,0.0009765625,392 +SQL,unique_values,0.0009534358978271484,392 +SQL,unique_values,0.002000093460083008,392 +SQL,unique_values,0.0010001659393310547,392 +SQL,unique_values,0.0010137557983398438,392 +SQL,min_max,0.0010013580322265625,392 +SQL,min_max,0.001039743423461914,392 +SQL,min_max,0.0009889602661132812,392 +SQL,min_max,0.0020008087158203125,392 +SQL,min_max,0.0010006427764892578,392 +SQL,scatter,0.0050160884857177734,392 +SQL,bar/line,0.005033731460571289,392 +SQL,scatter,0.0039980411529541016,392 +SQL,scatter,0.005013465881347656,392 +SQL,bar/line,0.004957675933837891,392 +SQL,scatter,0.0040395259857177734,392 diff --git a/tests/test_compiler.py b/tests/test_compiler.py index 3570a35e..e798f0a7 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -31,19 +31,21 @@ def test_underspecified_no_vis(test_recs): test_recs(df, no_vis_actions) assert len(df.current_vis) == 0 + # test for sql executor + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") - #test for sql executor - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") + test_recs(sql_df, no_vis_actions) + assert len(sql_df.current_vis) == 0 - test_recs(sql_df, no_vis_actions) - assert len(sql_df.current_vis) == 0 + # test only one filter context case. + sql_df.set_intent([lux.Clause(attribute="origin", filter_op="=", value="USA")]) + test_recs(sql_df, no_vis_actions) + assert len(sql_df.current_vis) == 0 - # test only one filter context case. - sql_df.set_intent([lux.Clause(attribute ="origin", filter_op="=", value="USA")]) - test_recs(sql_df, no_vis_actions) - assert len(sql_df.current_vis) == 0 def test_underspecified_single_vis(test_recs): one_vis_actions = ["Enhance", "Filter", "Generalize"] @@ -57,49 +59,54 @@ def test_underspecified_single_vis(test_recs): for attr in df.current_vis[0]._inferred_intent: assert attr.data_type == "quantitative" - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - sql_df.set_intent([lux.Clause(attribute ="milespergal"), lux.Clause(attribute ="weight")]) - test_recs(sql_df, one_vis_actions) - assert len(sql_df.current_vis) == 1 - assert sql_df.current_vis[0].mark == "scatter" - for attr in sql_df.current_vis[0]._inferred_intent: - assert attr.data_model == "measure" - for attr in sql_df.current_vis[0]._inferred_intent: - assert attr.data_type == "quantitative" + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + sql_df.set_intent( + [lux.Clause(attribute="milespergal"), lux.Clause(attribute="weight")] + ) + test_recs(sql_df, one_vis_actions) + assert len(sql_df.current_vis) == 1 + assert sql_df.current_vis[0].mark == "scatter" + for attr in sql_df.current_vis[0]._inferred_intent: + assert attr.data_model == "measure" + for attr in sql_df.current_vis[0]._inferred_intent: + assert attr.data_type == "quantitative" + # def test_underspecified_vis_collection(test_recs): -# multiple_vis_actions = ["Current viss"] - -# df = pd.read_csv("lux/data/car.csv") -# df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - -# df.set_intent([lux.Clause(attribute = ["Horsepower", "Weight", "Acceleration"]), lux.Clause(attribute ="Year", channel="x")]) -# assert len(df.current_vis) == 3 -# assert df.current_vis[0].mark == "line" -# for vlist in df.current_vis: -# assert (vlist.get_attr_by_channel("x")[0].attribute == "Year") -# test_recs(df, multiple_vis_actions) - -# df.set_intent([lux.Clause(attribute ="?"), lux.Clause(attribute ="Year", channel="x")]) -# assert len(df.current_vis) == len(list(df.columns)) - 1 # we remove year by year so its 8 vis instead of 9 -# for vlist in df.current_vis: -# assert (vlist.get_attr_by_channel("x")[0].attribute == "Year") -# test_recs(df, multiple_vis_actions) - -# df.set_intent([lux.Clause(attribute ="?", data_type="quantitative"), lux.Clause(attribute ="Year")]) -# assert len(df.current_vis) == len([vis.get_attr_by_data_type("quantitative") for vis in df.current_vis]) # should be 5 -# test_recs(df, multiple_vis_actions) - -# df.set_intent([lux.Clause(attribute ="?", data_model="measure"), lux.Clause(attribute="MilesPerGal", channel="y")]) -# for vlist in df.current_vis: -# print (vlist.get_attr_by_channel("y")[0].attribute == "MilesPerGal") -# test_recs(df, multiple_vis_actions) - -# df.set_intent([lux.Clause(attribute ="?", data_model="measure"), lux.Clause(attribute ="?", data_model="measure")]) -# assert len(df.current_vis) == len([vis.get_attr_by_data_model("measure") for vis in df.current_vis]) #should be 25 -# test_recs(df, multiple_vis_actions) +# multiple_vis_actions = ["Current viss"] + +# df = pd.read_csv("lux/data/car.csv") +# df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype + +# df.set_intent([lux.Clause(attribute = ["Horsepower", "Weight", "Acceleration"]), lux.Clause(attribute ="Year", channel="x")]) +# assert len(df.current_vis) == 3 +# assert df.current_vis[0].mark == "line" +# for vlist in df.current_vis: +# assert (vlist.get_attr_by_channel("x")[0].attribute == "Year") +# test_recs(df, multiple_vis_actions) + +# df.set_intent([lux.Clause(attribute ="?"), lux.Clause(attribute ="Year", channel="x")]) +# assert len(df.current_vis) == len(list(df.columns)) - 1 # we remove year by year so its 8 vis instead of 9 +# for vlist in df.current_vis: +# assert (vlist.get_attr_by_channel("x")[0].attribute == "Year") +# test_recs(df, multiple_vis_actions) + +# df.set_intent([lux.Clause(attribute ="?", data_type="quantitative"), lux.Clause(attribute ="Year")]) +# assert len(df.current_vis) == len([vis.get_attr_by_data_type("quantitative") for vis in df.current_vis]) # should be 5 +# test_recs(df, multiple_vis_actions) + +# df.set_intent([lux.Clause(attribute ="?", data_model="measure"), lux.Clause(attribute="MilesPerGal", channel="y")]) +# for vlist in df.current_vis: +# print (vlist.get_attr_by_channel("y")[0].attribute == "MilesPerGal") +# test_recs(df, multiple_vis_actions) + +# df.set_intent([lux.Clause(attribute ="?", data_model="measure"), lux.Clause(attribute ="?", data_model="measure")]) +# assert len(df.current_vis) == len([vis.get_attr_by_data_model("measure") for vis in df.current_vis]) #should be 25 +# test_recs(df, multiple_vis_actions) def test_set_intent_as_vis(test_recs): df = pd.read_csv("lux/data/car.csv") df._repr_html_() @@ -108,15 +115,17 @@ def test_set_intent_as_vis(test_recs): df._repr_html_() test_recs(df, ["Enhance", "Filter", "Generalize"]) + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + sql_df._repr_html_() + vis = sql_df.recommendation["Correlation"][0] + sql_df.intent = vis + sql_df._repr_html_() + test_recs(sql_df, ["Enhance", "Filter", "Generalize"]) - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - sql_df._repr_html_() - vis = sql_df.recommendation["Correlation"][0] - sql_df.intent = vis - sql_df._repr_html_() - test_recs(sql_df,["Enhance","Filter","Generalize"]) @pytest.fixture def test_recs(): @@ -138,21 +147,28 @@ def test_parse(): vlst = VisList([lux.Clause("Origin=?"), lux.Clause("MilesPerGal")], df) assert len(vlst) == 3 - df = pd.read_csv("lux/data/car.csv") - vlst = VisList([lux.Clause("Origin=?"), lux.Clause("MilesPerGal")],df) - assert len(vlst) == 3 + df = pd.read_csv("lux/data/car.csv") + vlst = VisList([lux.Clause("Origin=?"), lux.Clause("MilesPerGal")], df) + assert len(vlst) == 3 - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - vlst = VisList([lux.Clause("origin=?"), lux.Clause(attribute ="milespergal")],sql_df) - assert len(vlst) == 3 + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vlst = VisList( + [lux.Clause("origin=?"), lux.Clause(attribute="milespergal")], sql_df + ) + assert len(vlst) == 3 + + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vlst = VisList([lux.Clause("origin=?"), lux.Clause("milespergal")], sql_df) + assert len(vlst) == 3 - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - vlst = VisList([lux.Clause("origin=?"), lux.Clause("milespergal")],sql_df) - assert len(vlst) == 3 def test_underspecified_vis_collection_zval(): # check if the number of charts is correct @@ -171,17 +187,20 @@ def test_underspecified_vis_collection_zval(): # vlst = VisList([lux.Clause(attribute = ["Origin","Cylinders"], filter_op="=",value="?"),lux.Clause(attribute = ["Horsepower"]),lux.Clause(attribute = "Weight")],df) # assert len(vlst) == 8 - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - vlst = VisList( - [ - lux.Clause(attribute ="origin", filter_op="=", value="?"), - lux.Clause(attribute ="milespergal") - ], - sql_df - ) - assert len(vlst) == 3 + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vlst = VisList( + [ + lux.Clause(attribute="origin", filter_op="=", value="?"), + lux.Clause(attribute="milespergal"), + ], + sql_df, + ) + assert len(vlst) == 3 + def test_sort_bar(): from lux.processor.Compiler import Compiler @@ -213,22 +232,40 @@ def test_sort_bar(): assert vis.mark == "bar" assert vis._inferred_intent[1].sort == "ascending" + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis( + [ + lux.Clause( + attribute="acceleration", data_model="measure", data_type="quantitative" + ), + lux.Clause(attribute="origin", data_model="dimension", data_type="nominal"), + ], + sql_df, + ) + assert vis.mark == "bar" + assert vis._inferred_intent[1].sort == "" - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - vis = Vis([lux.Clause(attribute="acceleration",data_model="measure",data_type="quantitative"), - lux.Clause(attribute="origin",data_model="dimension",data_type="nominal")],sql_df) - assert vis.mark == "bar" - assert vis._inferred_intent[1].sort == '' + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis( + [ + lux.Clause( + attribute="acceleration", data_model="measure", data_type="quantitative" + ), + lux.Clause(attribute="name", data_model="dimension", data_type="nominal"), + ], + sql_df, + ) + assert vis.mark == "bar" + assert vis._inferred_intent[1].sort == "ascending" - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - vis = Vis([lux.Clause(attribute="acceleration",data_model="measure",data_type="quantitative"), - lux.Clause(attribute="name",data_model="dimension",data_type="nominal")],sql_df) - assert vis.mark == "bar" - assert vis._inferred_intent[1].sort == 'ascending' def test_specified_vis_collection(): df = pd.read_csv("lux/data/car.csv") @@ -316,57 +353,104 @@ def test_autoencoding_scatter(): ] ) - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - visList = VisList([lux.Clause(attribute="?"),lux.Clause(attribute="milespergal",channel="x")],sql_df) - for vis in visList: - check_attribute_on_channel(vis, "milespergal", "x") + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + visList = VisList( + [lux.Clause(attribute="?"), lux.Clause(attribute="milespergal", channel="x")], + sql_df, + ) + for vis in visList: + check_attribute_on_channel(vis, "milespergal", "x") + def test_autoencoding_scatter(): - # No channel specified - df = pd.read_csv("lux/data/car.csv") - df["Year"] = pd.to_datetime(df["Year"], format='%Y') # change pandas dtype for the column "Year" to datetype - vis = Vis([lux.Clause(attribute="MilesPerGal"), lux.Clause(attribute="Weight")],df) - check_attribute_on_channel(vis, "MilesPerGal", "x") - check_attribute_on_channel(vis, "Weight", "y") - - # Partial channel specified - vis = Vis([lux.Clause(attribute="MilesPerGal", channel="y"), lux.Clause(attribute="Weight")],df) - check_attribute_on_channel(vis, "MilesPerGal", "y") - check_attribute_on_channel(vis, "Weight", "x") - - # Full channel specified - vis = Vis([lux.Clause(attribute="MilesPerGal", channel="y"), lux.Clause(attribute="Weight", channel="x")],df) - check_attribute_on_channel(vis, "MilesPerGal", "y") - check_attribute_on_channel(vis, "Weight", "x") - # Duplicate channel specified - with pytest.raises(ValueError): - # Should throw error because there should not be columns with the same channel specified - df.set_intent([lux.Clause(attribute="MilesPerGal", channel="x"), lux.Clause(attribute="Weight", channel="x")]) - - #test for sql executor - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - vis = Vis([lux.Clause(attribute="milespergal"), lux.Clause(attribute="weight")],sql_df) - check_attribute_on_channel(vis, "milespergal", "x") - check_attribute_on_channel(vis, "weight", "y") - - # Partial channel specified - vis = Vis([lux.Clause(attribute="milespergal", channel="y"), lux.Clause(attribute="weight")],sql_df) - check_attribute_on_channel(vis, "milespergal", "y") - check_attribute_on_channel(vis, "weight", "x") - - # Full channel specified - vis = Vis([lux.Clause(attribute="milespergal", channel="y"), lux.Clause(attribute="weight", channel="x")],sql_df) - check_attribute_on_channel(vis, "milespergal", "y") - check_attribute_on_channel(vis, "weight", "x") - # Duplicate channel specified - with pytest.raises(ValueError): - # Should throw error because there should not be columns with the same channel specified - sql_df.set_intent([lux.Clause(attribute="milespergal", channel="x"), lux.Clause(attribute="weight", channel="x")]) - + # No channel specified + df = pd.read_csv("lux/data/car.csv") + df["Year"] = pd.to_datetime( + df["Year"], format="%Y" + ) # change pandas dtype for the column "Year" to datetype + vis = Vis([lux.Clause(attribute="MilesPerGal"), lux.Clause(attribute="Weight")], df) + check_attribute_on_channel(vis, "MilesPerGal", "x") + check_attribute_on_channel(vis, "Weight", "y") + + # Partial channel specified + vis = Vis( + [ + lux.Clause(attribute="MilesPerGal", channel="y"), + lux.Clause(attribute="Weight"), + ], + df, + ) + check_attribute_on_channel(vis, "MilesPerGal", "y") + check_attribute_on_channel(vis, "Weight", "x") + + # Full channel specified + vis = Vis( + [ + lux.Clause(attribute="MilesPerGal", channel="y"), + lux.Clause(attribute="Weight", channel="x"), + ], + df, + ) + check_attribute_on_channel(vis, "MilesPerGal", "y") + check_attribute_on_channel(vis, "Weight", "x") + # Duplicate channel specified + with pytest.raises(ValueError): + # Should throw error because there should not be columns with the same channel specified + df.set_intent( + [ + lux.Clause(attribute="MilesPerGal", channel="x"), + lux.Clause(attribute="Weight", channel="x"), + ] + ) + + # test for sql executor + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis( + [lux.Clause(attribute="milespergal"), lux.Clause(attribute="weight")], sql_df + ) + check_attribute_on_channel(vis, "milespergal", "x") + check_attribute_on_channel(vis, "weight", "y") + + # Partial channel specified + vis = Vis( + [ + lux.Clause(attribute="milespergal", channel="y"), + lux.Clause(attribute="weight"), + ], + sql_df, + ) + check_attribute_on_channel(vis, "milespergal", "y") + check_attribute_on_channel(vis, "weight", "x") + + # Full channel specified + vis = Vis( + [ + lux.Clause(attribute="milespergal", channel="y"), + lux.Clause(attribute="weight", channel="x"), + ], + sql_df, + ) + check_attribute_on_channel(vis, "milespergal", "y") + check_attribute_on_channel(vis, "weight", "x") + # Duplicate channel specified + with pytest.raises(ValueError): + # Should throw error because there should not be columns with the same channel specified + sql_df.set_intent( + [ + lux.Clause(attribute="milespergal", channel="x"), + lux.Clause(attribute="weight", channel="x"), + ] + ) + + def test_autoencoding_histogram(): # No channel specified df = pd.read_csv("lux/data/car.csv") @@ -380,18 +464,20 @@ def test_autoencoding_histogram(): assert vis.get_attr_by_channel("x")[0].attribute == "MilesPerGal" assert vis.get_attr_by_channel("y")[0].attribute == "Record" + # No channel specified + # test for sql executor + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute="milespergal", channel="y")], sql_df) + check_attribute_on_channel(vis, "milespergal", "y") - # No channel specified - #test for sql executor - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - vis = Vis([lux.Clause(attribute="milespergal", channel="y")],sql_df) - check_attribute_on_channel(vis, "milespergal", "y") + vis = Vis([lux.Clause(attribute="milespergal", channel="x")], sql_df) + assert vis.get_attr_by_channel("x")[0].attribute == "milespergal" + assert vis.get_attr_by_channel("y")[0].attribute == "Record" - vis = Vis([lux.Clause(attribute="milespergal",channel="x")],sql_df) - assert vis.get_attr_by_channel("x")[0].attribute == "milespergal" - assert vis.get_attr_by_channel("y")[0].attribute == "Record" def test_autoencoding_line_chart(): df = pd.read_csv("lux/data/car.csv") @@ -433,28 +519,49 @@ def test_autoencoding_line_chart(): ] ) + # test for sql executor + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis( + [lux.Clause(attribute="year"), lux.Clause(attribute="acceleration")], sql_df + ) + check_attribute_on_channel(vis, "year", "x") + check_attribute_on_channel(vis, "acceleration", "y") - #test for sql executor - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - vis = Vis([lux.Clause(attribute="year"), lux.Clause(attribute="acceleration")],sql_df) - check_attribute_on_channel(vis, "year", "x") - check_attribute_on_channel(vis, "acceleration", "y") + # Partial channel specified + vis = Vis( + [ + lux.Clause(attribute="year", channel="y"), + lux.Clause(attribute="acceleration"), + ], + sql_df, + ) + check_attribute_on_channel(vis, "year", "y") + check_attribute_on_channel(vis, "acceleration", "x") - # Partial channel specified - vis = Vis([lux.Clause(attribute="year", channel="y"), lux.Clause(attribute="acceleration")],sql_df) - check_attribute_on_channel(vis, "year", "y") - check_attribute_on_channel(vis, "acceleration", "x") + # Full channel specified + vis = Vis( + [ + lux.Clause(attribute="year", channel="y"), + lux.Clause(attribute="acceleration", channel="x"), + ], + sql_df, + ) + check_attribute_on_channel(vis, "year", "y") + check_attribute_on_channel(vis, "acceleration", "x") - # Full channel specified - vis = Vis([lux.Clause(attribute="year", channel="y"), lux.Clause(attribute="acceleration", channel="x")],sql_df) - check_attribute_on_channel(vis, "year", "y") - check_attribute_on_channel(vis, "acceleration", "x") + with pytest.raises(ValueError): + # Should throw error because there should not be columns with the same channel specified + sql_df.set_intent( + [ + lux.Clause(attribute="year", channel="x"), + lux.Clause(attribute="acceleration", channel="x"), + ] + ) - with pytest.raises(ValueError): - # Should throw error because there should not be columns with the same channel specified - sql_df.set_intent([lux.Clause(attribute="year", channel="x"), lux.Clause(attribute="acceleration", channel="x")]) def test_autoencoding_color_line_chart(): df = pd.read_csv("lux/data/car.csv") @@ -471,16 +578,22 @@ def test_autoencoding_color_line_chart(): check_attribute_on_channel(vis, "Acceleration", "y") check_attribute_on_channel(vis, "Origin", "color") + # test for sql executor + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + intent = [ + lux.Clause(attribute="year"), + lux.Clause(attribute="acceleration"), + lux.Clause(attribute="origin"), + ] + vis = Vis(intent, sql_df) + check_attribute_on_channel(vis, "year", "x") + check_attribute_on_channel(vis, "acceleration", "y") + check_attribute_on_channel(vis, "origin", "color") - #test for sql executor - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - intent = [lux.Clause(attribute="year"), lux.Clause(attribute="acceleration"), lux.Clause(attribute="origin")] - vis = Vis(intent,sql_df) - check_attribute_on_channel(vis, "year", "x") - check_attribute_on_channel(vis, "acceleration", "y") - check_attribute_on_channel(vis, "origin", "color") def test_autoencoding_color_scatter_chart(): df = pd.read_csv("lux/data/car.csv") @@ -507,16 +620,32 @@ def test_autoencoding_color_scatter_chart(): ) check_attribute_on_channel(vis, "Acceleration", "color") + # test for sql executor + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis( + [ + lux.Clause(attribute="horsepower"), + lux.Clause(attribute="acceleration"), + lux.Clause(attribute="origin"), + ], + sql_df, + ) + check_attribute_on_channel(vis, "origin", "color") - #test for sql executor - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - vis = Vis([lux.Clause(attribute="horsepower"), lux.Clause(attribute="acceleration"), lux.Clause(attribute="origin")],sql_df) - check_attribute_on_channel(vis, "origin", "color") + vis = Vis( + [ + lux.Clause(attribute="horsepower"), + lux.Clause(attribute="acceleration", channel="color"), + lux.Clause(attribute="origin"), + ], + sql_df, + ) + check_attribute_on_channel(vis, "acceleration", "color") - vis = Vis([lux.Clause(attribute="horsepower"), lux.Clause(attribute="acceleration", channel="color"), lux.Clause(attribute="origin")],sql_df) - check_attribute_on_channel(vis, "acceleration", "color") def test_populate_options(): from lux.processor.Compiler import Compiler @@ -545,25 +674,39 @@ def test_populate_options(): ["Acceleration", "Weight", "Horsepower", "MilesPerGal", "Displacement"], ) + # test for sql executor + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + sql_df.set_intent([lux.Clause(attribute="?"), lux.Clause(attribute="milespergal")]) + col_set = set() + for specOptions in Compiler.populate_wildcard_options(sql_df._intent, sql_df)[ + "attributes" + ]: + for clause in specOptions: + col_set.add(clause.attribute) + assert list_equal(list(col_set), list(sql_df.columns)) - #test for sql executor - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - sql_df.set_intent([lux.Clause(attribute="?"), lux.Clause(attribute="milespergal")]) - col_set = set() - for specOptions in Compiler.populate_wildcard_options(sql_df._intent, sql_df)["attributes"]: - for clause in specOptions: - col_set.add(clause.attribute) - assert list_equal(list(col_set), list(sql_df.columns)) + sql_df.set_intent( + [ + lux.Clause(attribute="?", data_model="measure"), + lux.Clause(attribute="milespergal"), + ] + ) + sql_df._repr_html_() + col_set = set() + for specOptions in Compiler.populate_wildcard_options(sql_df._intent, sql_df)[ + "attributes" + ]: + for clause in specOptions: + col_set.add(clause.attribute) + assert list_equal( + list(col_set), + ["acceleration", "weight", "horsepower", "milespergal", "displacement"], + ) - sql_df.set_intent([lux.Clause(attribute="?", data_model="measure"), lux.Clause(attribute="milespergal")]) - sql_df._repr_html_() - col_set = set() - for specOptions in Compiler.populate_wildcard_options(sql_df._intent, sql_df)["attributes"]: - for clause in specOptions: - col_set.add(clause.attribute) - assert list_equal(list(col_set), ['acceleration', 'weight', 'horsepower', 'milespergal', 'displacement']) def test_remove_all_invalid(): df = pd.read_csv("lux/data/car.csv") @@ -578,15 +721,22 @@ def test_remove_all_invalid(): df._repr_html_() assert len(df.current_vis) == 0 + # test for sql executor + connection = psycopg2.connect( + "host=localhost dbname=adventureworks user=postgres password=lux" + ) + sql_df = pd.DataFrame() + sql_df.set_SQL_connection(connection, "car") + # with pytest.warns(UserWarning,match="duplicate attribute specified in the intent"): + sql_df.set_intent( + [ + lux.Clause(attribute="origin", filter_op="=", value="USA"), + lux.Clause(attribute="origin"), + ] + ) + sql_df._repr_html_() + assert len(sql_df.current_vis) == 0 - #test for sql executor - connection = psycopg2.connect("host=localhost dbname=adventureworks user=postgres password=lux") - sql_df = pd.DataFrame() - sql_df.set_SQL_connection(connection, "car") - # with pytest.warns(UserWarning,match="duplicate attribute specified in the intent"): - sql_df.set_intent([lux.Clause(attribute = "origin", filter_op="=",value="USA"),lux.Clause(attribute = "origin")]) - sql_df._repr_html_() - assert len(sql_df.current_vis)==0 def list_equal(l1, l2): l1.sort() diff --git a/tests/test_sql_executor.py b/tests/test_sql_executor.py index 1cf31775..2bbab7b7 100644 --- a/tests/test_sql_executor.py +++ b/tests/test_sql_executor.py @@ -1,10 +1,10 @@ # 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 +# 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, @@ -20,152 +20,223 @@ from lux.vis.VisList import VisList import psycopg2 -def test_lazy_execution(): - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") - - intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] - vis = Vis(intent) - # Check data field in vis is empty before calling executor - assert vis.data is None - SQLExecutor.execute([vis], sql_df) - assert type(vis.data) == lux.core.frame.LuxDataFrame - -def test_selection(): - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") - intent = [lux.Clause(attribute = ["horsepower", "weight", "acceleration"]), lux.Clause(attribute ="year")] - vislist = VisList(intent,sql_df) - assert all([type(vis.data) == lux.core.frame.LuxDataFrame for vis in vislist]) - assert all(vislist[2].data.columns == ["year", 'acceleration']) - -def test_aggregation(): - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") +def test_lazy_execution(): + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + + intent = [ + lux.Clause(attribute="horsepower", aggregation="mean"), + lux.Clause(attribute="origin"), + ] + vis = Vis(intent) + # Check data field in vis is empty before calling executor + assert vis.data is None + SQLExecutor.execute([vis], sql_df) + assert type(vis.data) == lux.core.frame.LuxDataFrame - intent = [lux.Clause(attribute ="horsepower", aggregation="mean"), lux.Clause(attribute ="origin")] - vis = Vis(intent,sql_df) - result_df = vis.data - assert int(result_df[result_df["origin"]=="USA"]["horsepower"])==119 - intent = [lux.Clause(attribute ="horsepower", aggregation="sum"), lux.Clause(attribute ="origin")] - vis = Vis(intent,sql_df) - result_df = vis.data - assert int(result_df[result_df["origin"]=="Japan"]["horsepower"])==6307 +def test_selection(): + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") - intent = [lux.Clause(attribute ="horsepower", aggregation="max"), lux.Clause(attribute ="origin")] - vis = Vis(intent,sql_df) - result_df = vis.data - assert int(result_df[result_df["origin"]=="Europe"]["horsepower"])==133 + intent = [ + lux.Clause(attribute=["horsepower", "weight", "acceleration"]), + lux.Clause(attribute="year"), + ] + vislist = VisList(intent, sql_df) + assert all([type(vis.data) == lux.core.frame.LuxDataFrame for vis in vislist]) + assert all(vislist[2].data.columns == ["year", "acceleration"]) -def test_colored_bar_chart(): - from lux.vis.Vis import Vis - from lux.vis.Vis import Clause - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") +def test_aggregation(): + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + + intent = [ + lux.Clause(attribute="horsepower", aggregation="mean"), + lux.Clause(attribute="origin"), + ] + vis = Vis(intent, sql_df) + result_df = vis.data + assert int(result_df[result_df["origin"] == "USA"]["horsepower"]) == 119 + + intent = [ + lux.Clause(attribute="horsepower", aggregation="sum"), + lux.Clause(attribute="origin"), + ] + vis = Vis(intent, sql_df) + result_df = vis.data + assert int(result_df[result_df["origin"] == "Japan"]["horsepower"]) == 6307 + + intent = [ + lux.Clause(attribute="horsepower", aggregation="max"), + lux.Clause(attribute="origin"), + ] + vis = Vis(intent, sql_df) + result_df = vis.data + assert int(result_df[result_df["origin"] == "Europe"]["horsepower"]) == 133 - x_clause = Clause(attribute = "milespergal", channel = "x") - y_clause = Clause(attribute = "origin", channel = "y") - color_clause = Clause(attribute = 'cylinders', channel = "color") - new_vis = Vis([x_clause, y_clause, color_clause],sql_df) - #make sure dimention of the data is correct - color_cardinality = len(sql_df.unique_values['cylinders']) - group_by_cardinality = len(sql_df.unique_values['origin']) - assert (len(new_vis.data.columns)==3) - assert(len(new_vis.data)==15 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values +def test_colored_bar_chart(): + from lux.vis.Vis import Vis + from lux.vis.Vis import Clause + + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + + x_clause = Clause(attribute="milespergal", channel="x") + y_clause = Clause(attribute="origin", channel="y") + color_clause = Clause(attribute="cylinders", channel="color") + + new_vis = Vis([x_clause, y_clause, color_clause], sql_df) + # make sure dimention of the data is correct + color_cardinality = len(sql_df.unique_values["cylinders"]) + group_by_cardinality = len(sql_df.unique_values["origin"]) + assert len(new_vis.data.columns) == 3 + assert ( + len(new_vis.data) + == 15 + > group_by_cardinality + < color_cardinality * group_by_cardinality + ) # Not color_cardinality*group_by_cardinality since some combinations have 0 values - def test_colored_line_chart(): - from lux.vis.Vis import Vis - from lux.vis.Vis import Clause + from lux.vis.Vis import Vis + from lux.vis.Vis import Clause + + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") + x_clause = Clause(attribute="year", channel="x") + y_clause = Clause(attribute="milespergal", channel="y") + color_clause = Clause(attribute="cylinders", channel="color") - x_clause = Clause(attribute = "year", channel = "x") - y_clause = Clause(attribute = "milespergal", channel = "y") - color_clause = Clause(attribute = 'cylinders', channel = "color") + new_vis = Vis([x_clause, y_clause, color_clause], sql_df) - new_vis = Vis([x_clause, y_clause, color_clause],sql_df) + # make sure dimention of the data is correct + color_cardinality = len(sql_df.unique_values["cylinders"]) + group_by_cardinality = len(sql_df.unique_values["year"]) + assert len(new_vis.data.columns) == 3 + assert ( + len(new_vis.data) + == 60 + > group_by_cardinality + < color_cardinality * group_by_cardinality + ) # Not color_cardinality*group_by_cardinality since some combinations have 0 values - #make sure dimention of the data is correct - color_cardinality = len(sql_df.unique_values['cylinders']) - group_by_cardinality = len(sql_df.unique_values['year']) - assert (len(new_vis.data.columns)==3) - assert(len(new_vis.data)==60 > group_by_cardinality < color_cardinality*group_by_cardinality) # Not color_cardinality*group_by_cardinality since some combinations have 0 values - def test_filter(): - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + + intent = [ + lux.Clause(attribute="horsepower"), + lux.Clause(attribute="year"), + lux.Clause(attribute="origin", filter_op="=", value="USA"), + ] + vis = Vis(intent, sql_df) + vis._vis_data = sql_df + filter_output = SQLExecutor.execute_filter(vis) + assert filter_output[0] == "WHERE origin = 'USA'" + assert filter_output[1] == ["origin"] - intent = [lux.Clause(attribute ="horsepower"), lux.Clause(attribute ="year"), lux.Clause(attribute ="origin", filter_op="=", value ="USA")] - vis = Vis(intent,sql_df) - vis._vis_data = sql_df - filter_output = SQLExecutor.execute_filter(vis) - assert filter_output[0] == "WHERE origin = 'USA'" - assert filter_output[1] == ['origin'] def test_inequalityfilter(): - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") - vis = Vis([lux.Clause(attribute ="horsepower", filter_op=">", value=50), lux.Clause(attribute ="milespergal")]) - vis._vis_data = sql_df - filter_output = SQLExecutor.execute_filter(vis) - assert filter_output[0] == "WHERE horsepower > '50'" - assert filter_output[1] == ['horsepower'] - - intent = [lux.Clause(attribute ="horsepower", filter_op="<=", value=100), lux.Clause(attribute ="milespergal")] - vis = Vis(intent,sql_df) - vis._vis_data = sql_df - filter_output = SQLExecutor.execute_filter(vis) - assert filter_output[0] == "WHERE horsepower <= '100'" - assert filter_output[1] == ['horsepower'] - + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis( + [ + lux.Clause(attribute="horsepower", filter_op=">", value=50), + lux.Clause(attribute="milespergal"), + ] + ) + vis._vis_data = sql_df + filter_output = SQLExecutor.execute_filter(vis) + assert filter_output[0] == "WHERE horsepower > '50'" + assert filter_output[1] == ["horsepower"] + + intent = [ + lux.Clause(attribute="horsepower", filter_op="<=", value=100), + lux.Clause(attribute="milespergal"), + ] + vis = Vis(intent, sql_df) + vis._vis_data = sql_df + filter_output = SQLExecutor.execute_filter(vis) + assert filter_output[0] == "WHERE horsepower <= '100'" + assert filter_output[1] == ["horsepower"] + + def test_binning(): - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") - vis = Vis([lux.Clause(attribute ="horsepower")],sql_df) - nbins =list(filter(lambda x: x.bin_size!=0, vis._inferred_intent))[0].bin_size - assert len(vis.data) == nbins + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute="horsepower")], sql_df) + nbins = list(filter(lambda x: x.bin_size != 0, vis._inferred_intent))[0].bin_size + assert len(vis.data) == nbins + def test_record(): - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") - vis = Vis([lux.Clause(attribute ="cylinders")],sql_df) - assert len(vis.data) == len(sql_df.unique_values["cylinders"]) - + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + vis = Vis([lux.Clause(attribute="cylinders")], sql_df) + assert len(vis.data) == len(sql_df.unique_values["cylinders"]) + + def test_filter_aggregation_fillzero_aligned(): - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") - intent = [lux.Clause(attribute="cylinders"), lux.Clause(attribute="milespergal"), lux.Clause("origin=Japan")] - vis = Vis(intent,sql_df) - result = vis.data - assert result[result["cylinders"]==5]["milespergal"].values[0]==0 - assert result[result["cylinders"]==8]["milespergal"].values[0]==0 + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + intent = [ + lux.Clause(attribute="cylinders"), + lux.Clause(attribute="milespergal"), + lux.Clause("origin=Japan"), + ] + vis = Vis(intent, sql_df) + result = vis.data + assert result[result["cylinders"] == 5]["milespergal"].values[0] == 0 + assert result[result["cylinders"] == 8]["milespergal"].values[0] == 0 + def test_exclude_attribute(): - connection = psycopg2.connect("host=localhost dbname=postgres_db user=postgres password=lux") - sql_df = lux.LuxDataFrame() - sql_df.set_SQL_connection(connection, "car") - intent = [lux.Clause("?", exclude=["name", "year"]), lux.Clause("horsepower")] - vislist = VisList(intent,sql_df) - for vis in vislist: - assert (vis.get_attr_by_channel("x")[0].attribute != "year") - assert (vis.get_attr_by_channel("x")[0].attribute != "name") - assert (vis.get_attr_by_channel("y")[0].attribute != "year") - assert (vis.get_attr_by_channel("y")[0].attribute != "year") + connection = psycopg2.connect( + "host=localhost dbname=postgres_db user=postgres password=lux" + ) + sql_df = lux.LuxDataFrame() + sql_df.set_SQL_connection(connection, "car") + intent = [lux.Clause("?", exclude=["name", "year"]), lux.Clause("horsepower")] + vislist = VisList(intent, sql_df) + for vis in vislist: + assert vis.get_attr_by_channel("x")[0].attribute != "year" + assert vis.get_attr_by_channel("x")[0].attribute != "name" + assert vis.get_attr_by_channel("y")[0].attribute != "year" + assert vis.get_attr_by_channel("y")[0].attribute != "year" From 9897d0e18c9ee0c775151e88cde40ba890732939 Mon Sep 17 00:00:00 2001 From: cjachekang <47467363+cjachekang@users.noreply.github.com> Date: Sun, 8 Nov 2020 18:11:27 -0800 Subject: [PATCH 041/114] Better warning message for Vis and VisList (#135) * added functionality to delete Vis * fixed deletion logic * add observer to automatically update deletions * able to refresh widget on setting intent * support for setting intent from frontend * quick fix to output * changed variable intentindex name * added better error msg for > 1 intent for vis * reverting some changes * adding warning message for Vis intents being > 1 * passes tests and intent < 3 * minor change to error message, added test * run black * accounted for more edge cases and hid traceback * fixed typo * added tests * format w/ black * ran black again * Update Vis.py minor readability changes Co-authored-by: Doris Lee --- lux/vis/Vis.py | 14 ++++++++++++++ tests/test_error_warning.py | 24 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lux/vis/Vis.py b/lux/vis/Vis.py index c4a4fd83..cb1e774f 100644 --- a/lux/vis/Vis.py +++ b/lux/vis/Vis.py @@ -326,6 +326,8 @@ def refresh_source(self, ldf): # -> Vis: PandasExecutor, ) # TODO: temporary (generalize to executor) + self.check_not_vislist_intent() + ldf.maintain_metadata() self._source = ldf self._inferred_intent = Parser.parse(self._intent) @@ -340,3 +342,15 @@ def refresh_source(self, ldf): # -> Vis: self._inferred_intent = vis._inferred_intent self._vis_data = vis.data self._min_max = vis._min_max + + def check_not_vislist_intent(self): + if len(self._intent) > 2 or "?" in self._intent: + for i in range(len(self._intent)): + if type(self._intent[i]) != Clause: + import sys + + sys.tracebacklimit = 0 + raise SyntaxError( + "The intent that you specified corresponds to more than one visualization. Please replace the Vis constructor with VisList to generate a list of visualizations. " + + "For more information, see: https://lux-api.readthedocs.io/en/latest/source/guide/vis.html#working-with-collections-of-visualization-with-vislist" + ) diff --git a/tests/test_error_warning.py b/tests/test_error_warning.py index e4e23c11..d5fe49ff 100644 --- a/tests/test_error_warning.py +++ b/tests/test_error_warning.py @@ -15,6 +15,7 @@ from .context import lux import pytest import pandas as pd +from lux.vis.Vis import Vis # Test suite for checking if the expected errors and warnings are showing up correctly def test_context_str_error(): @@ -35,6 +36,29 @@ def test_bad_filter(): df[df["Region"] == "asdfgh"]._repr_html_() +def test_multi_vis(): + df = pd.read_csv("lux/data/college.csv") + with pytest.raises( + SyntaxError, + match="The intent that you specified corresponds to more than one visualization.", + ): + Vis(["SATAverage", "AverageCost", "Geography=?"], df)._repr_html_() + + with pytest.raises( + SyntaxError, + match="The intent that you specified corresponds to more than one visualization.", + ): + Vis(["SATAverage", "?"], df)._repr_html_() + + with pytest.raises( + SyntaxError, + match="The intent that you specified corresponds to more than one visualization.", + ): + Vis( + ["SATAverage", "AverageCost", "Region=New England|Southeast"], df + )._repr_html_() + + # Test Properties with Private Variables Readable but not Writable def test_vis_private_properties(): from lux.vis.Vis import Vis From af0043a3619eac15e962a4270f86f47affa5f126 Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Wed, 11 Nov 2020 15:00:41 +0800 Subject: [PATCH 042/114] Pandas rewrite Performance optimizations (#136) * basic scatterplot experiments * experiment results with manually binned heatmaps * experiment result * incorporated heatmap code into executor and renderer * additional experiments to evaluate scatter v.s. heatmap performance * experiment based on real estate and airbnb data * modified general sampling criteria, suppress SettingWithCopyWarning stemming from groupby .agg (#93) * decrease sampling parameter * change sampling strategy (above threshold keep 3/4 of data) * remove experiment dir * modified performance param * enforce lux-widget minimum version * update requirement.txt * testing out modin (Recursion error) * create modin executor, all else in sync with master changes * rewrote .loc with column reference, speed up by 100x * replace agg("count") with .count() --> ~0.1ms speedup * run black --- lux/executor/PandasExecutor.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/lux/executor/PandasExecutor.py b/lux/executor/PandasExecutor.py index 64fa2e54..f396c86b 100644 --- a/lux/executor/PandasExecutor.py +++ b/lux/executor/PandasExecutor.py @@ -332,15 +332,11 @@ def apply_filter( def execute_2D_binning(vis: Vis): pd.reset_option("mode.chained_assignment") with pd.option_context("mode.chained_assignment", None): - x_attr = vis.get_attr_by_channel("x")[0] - y_attr = vis.get_attr_by_channel("y")[0] + x_attr = vis.get_attr_by_channel("x")[0].attribute + y_attr = vis.get_attr_by_channel("y")[0].attribute - vis._vis_data.loc[:, "xBin"] = pd.cut( - vis._vis_data[x_attr.attribute], bins=40 - ) - vis._vis_data.loc[:, "yBin"] = pd.cut( - vis._vis_data[y_attr.attribute], bins=40 - ) + vis._vis_data["xBin"] = pd.cut(vis._vis_data[x_attr], bins=40) + vis._vis_data["yBin"] = pd.cut(vis._vis_data[y_attr], bins=40) color_attr = vis.get_attr_by_channel("color") if len(color_attr) > 0: @@ -361,23 +357,17 @@ def execute_2D_binning(vis: Vis): ).reset_index() result = result.dropna() else: - groups = vis._vis_data.groupby(["xBin", "yBin"])[x_attr.attribute] - result = groups.agg("count").reset_index( - name=x_attr.attribute - ) # .agg in this line throws SettingWithCopyWarning - result = result.rename(columns={x_attr.attribute: "count"}) + groups = vis._vis_data.groupby(["xBin", "yBin"])[x_attr] + result = groups.count().reset_index(name=x_attr) + result = result.rename(columns={x_attr: "count"}) result = result[result["count"] != 0] # convert type to facilitate weighted correlation interestingess calculation - result.loc[:, "xBinStart"] = ( - result["xBin"].apply(lambda x: x.left).astype("float") - ) - result.loc[:, "xBinEnd"] = result["xBin"].apply(lambda x: x.right) + result["xBinStart"] = result["xBin"].apply(lambda x: x.left).astype("float") + result["xBinEnd"] = result["xBin"].apply(lambda x: x.right) - result.loc[:, "yBinStart"] = ( - result["yBin"].apply(lambda x: x.left).astype("float") - ) - result.loc[:, "yBinEnd"] = result["yBin"].apply(lambda x: x.right) + result["yBinStart"] = result["yBin"].apply(lambda x: x.left).astype("float") + result["yBinEnd"] = result["yBin"].apply(lambda x: x.right) vis._vis_data = result.drop(columns=["xBin", "yBin"]) From 907f2159999ff340be1664ebd50a19c156200930 Mon Sep 17 00:00:00 2001 From: 19thyneb Date: Sat, 14 Nov 2020 16:55:16 -0800 Subject: [PATCH 043/114] Added 2D Binning functionality to SQL Executor added 2D binning to replace scatterplots when using SQL executor. --- lux/executor/SQLExecutor.py | 102 +++++++++++++++++++++++++++- lux/vis/Vis.py | 2 + lux/vislib/altair/AltairRenderer.py | 3 +- 3 files changed, 102 insertions(+), 5 deletions(-) diff --git a/lux/executor/SQLExecutor.py b/lux/executor/SQLExecutor.py index 40424887..f9e92304 100644 --- a/lux/executor/SQLExecutor.py +++ b/lux/executor/SQLExecutor.py @@ -42,7 +42,7 @@ def execute(view_collection: VisList, ldf: LuxDataFrame): if clause.attribute: if clause.attribute != "Record": attributes.add(clause.attribute) - if view.mark not in ["bar", "line", "histogram"]: + if view.mark == "scatter": start = time.time() where_clause, filterVars = SQLExecutor.execute_filter(view) @@ -52,7 +52,11 @@ def execute(view_collection: VisList, ldf: LuxDataFrame): ), ldf.SQLconnection, ) - + ldf._message.add_unique( + f"Large scatterplots detected: Lux is automatically binning scatterplots to heatmaps.", + priority=98, + ) + #SQLExecutor.execute_2D_binning(view, ldf) required_variables = attributes | set(filterVars) required_variables = ",".join(required_variables) row_count = list( @@ -89,6 +93,11 @@ def execute(view_collection: VisList, ldf: LuxDataFrame): header=False, index=False, ) + view._postbin = True + ldf._message.add_unique( + f"Large scatterplots detected: Lux is automatically binning scatterplots to heatmaps.", + priority=98, + ) if view.mark == "bar" or view.mark == "line": start = time.time() SQLExecutor.execute_aggregate(view, ldf) @@ -114,7 +123,7 @@ def execute(view_collection: VisList, ldf: LuxDataFrame): # append benchmark data to file benchmark_data = { "executor_name": ["SQL"], - "query_action": ["binning"], + "query_action": ["histogram"], "time": [end - start], "length": [ldf.length], } @@ -442,6 +451,93 @@ def execute_binning(view: Vis, ldf: LuxDataFrame): view._vis_data = utils.pandas_to_lux(view.data) view._vis_data.length = list(length_query["length"])[0] + @staticmethod + def execute_2D_binning(view: Vis, ldf:LuxDataFrame): + import numpy as np + + x_attribute = list(filter(lambda x: x.channel == "x", view._inferred_intent))[ + 0 + ] + + y_attribute = list(filter(lambda x: x.channel == "y", view._inferred_intent))[ + 0 + ] + + num_bins = 40 + x_attr_min = ldf._min_max[x_attribute.attribute][0] + x_attr_max = ldf._min_max[x_attribute.attribute][1] + x_attr_type = type(ldf.unique_values[x_attribute.attribute][0]) + + y_attr_min = ldf._min_max[y_attribute.attribute][0] + y_attr_max = ldf._min_max[y_attribute.attribute][1] + y_attr_type = type(ldf.unique_values[y_attribute.attribute][0]) + + # get filters if available + where_clause, filterVars = SQLExecutor.execute_filter(view) + + # length_query = pandas.read_sql( + # "SELECT COUNT(*) as length FROM {} {}".format(ldf.table_name, where_clause), + # ldf.SQLconnection, + # ) + + # need to calculate the bin edges before querying for the relevant data + x_bin_width = (x_attr_max - x_attr_min) / num_bins + y_bin_width = (y_attr_max - y_attr_min) / num_bins + + x_upper_edges = [] + y_upper_edges = [] + for e in range(1, num_bins): + x_curr_edge = x_attr_min + e * x_bin_width + y_curr_edge = y_attr_min + e * y_bin_width + #get upper edges for x attribute bins + if x_attr_type == int: + x_upper_edges.append(str(math.ceil(x_curr_edge))) + else: + x_upper_edges.append(str(x_curr_edge)) + + #get upper edges for y attribute bins + if y_attr_type == int: + y_upper_edges.append(str(math.ceil(y_curr_edge))) + else: + y_upper_edges.append(str(y_curr_edge)) + x_upper_edges_string = ",".join(x_upper_edges) + y_upper_edges_string = ",".join(y_upper_edges) + #view_filter, filter_vars = SQLExecutor.execute_filter(view) + + #create a new where clause that will include the filter for each x axis bin + bin_count_data = [] + for c in range(0, len(y_upper_edges)): + if len(where_clause) > 1: + bin_where_clause = where_clause + " AND " + else: + bin_where_clause = "WHERE " + if c == 0: + lower_bound = str(x_attr_min) + lower_bound_clause = x_attribute.attribute + " >= " + "'" + lower_bound + "'" + else: + lower_bound = str(x_upper_edges[c-1]) + lower_bound_clause = x_attribute.attribute + " >= " + "'" + lower_bound + "'" + upper_bound = str(x_upper_edges[c]) + upper_bound_clause = x_attribute.attribute + " < " + "'" + upper_bound + "'" + bin_where_clause = bin_where_clause + lower_bound_clause + " AND " + upper_bound_clause + + bin_count_query = "SELECT width_bucket, COUNT(width_bucket) FROM (SELECT width_bucket({}, '{}') FROM {} {}) as Buckets GROUP BY width_bucket ORDER BY width_bucket".format( + y_attribute.attribute, + "{" + y_upper_edges_string + "}", + ldf.table_name, + bin_where_clause, + ) + curr_column_data = pandas.read_sql(bin_count_query, ldf.SQLconnection) + if len(curr_column_data) > 0: + curr_column_data['xBinStart'] = lower_bound + curr_column_data['xBinEnd'] = upper_bound + curr_column_data['yBinStart'] = curr_column_data.apply(lambda row: float(y_upper_edges[row['width_bucket']-1])-y_bin_width, axis = 1) + curr_column_data['yBinEnd'] = curr_column_data.apply(lambda row: float(y_upper_edges[row['width_bucket']-1]), axis = 1) + bin_count_data.append(curr_column_data) + output = pandas.concat(bin_count_data) + output = output.drop(['width_bucket'], axis = 1).to_pandas() + view._vis_data = output + @staticmethod # takes in a view and returns an appropriate SQL WHERE clause that based on the filters specified in the view's _inferred_intent def execute_filter(view: Vis): diff --git a/lux/vis/Vis.py b/lux/vis/Vis.py index c4a4fd83..02e06184 100644 --- a/lux/vis/Vis.py +++ b/lux/vis/Vis.py @@ -340,3 +340,5 @@ def refresh_source(self, ldf): # -> Vis: self._inferred_intent = vis._inferred_intent self._vis_data = vis.data self._min_max = vis._min_max + self._postbin = vis._postbin + self.data.executor = vis.data.executor diff --git a/lux/vislib/altair/AltairRenderer.py b/lux/vislib/altair/AltairRenderer.py index 2692f72e..3a00066e 100644 --- a/lux/vislib/altair/AltairRenderer.py +++ b/lux/vislib/altair/AltairRenderer.py @@ -51,9 +51,8 @@ def create_vis(self, vis, standalone=True): # Lazy Evaluation for 2D Binning if vis.mark == "scatter" and vis._postbin: vis._mark = "heatmap" - from lux.executor.PandasExecutor import PandasExecutor - PandasExecutor.execute_2D_binning(vis) + vis.data.executor.execute_2D_binning(vis) # If a column has a Period dtype, or contains Period objects, convert it back to Datetime if vis.data is not None: for attr in list(vis.data.columns): From 603e3d7a99025f44ea9b5143fd873c292506b38b Mon Sep 17 00:00:00 2001 From: Doris Lee Date: Sun, 15 Nov 2020 21:00:07 +0800 Subject: [PATCH 044/114] Update README.md update slack link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7f3b1ff..b4a5a7f5 100644 --- a/README.md +++ b/README.md @@ -149,5 +149,5 @@ Other additional resources: - Visit [ReadTheDoc](https://lux-api.readthedocs.io/en/latest/) for more detailed documentation. - Clone [lux-binder](https://github.com/lux-org/lux-binder) to try out these [hands-on exercises](https://github.com/lux-org/lux-binder/tree/master/exercise) or a more comprehensive [tutorial series](https://github.com/lux-org/lux-binder/tree/master/tutorial) on how to use Lux. -- Join our [Slack channel](http://lux-project.slack.com/) for support and discussion. +- Join our [Slack channel](https://lux-project.slack.com/join/shared_invite/zt-iwg84wfb-fBPaGTBBZfkb9arziy3W~g) for support and discussion. - Report any bugs, issues, or requests through [Github Issues](https://github.com/lux-org/lux/issues). From 852c6a175cf363a9cb21051d6326d49c29923a84 Mon Sep 17 00:00:00 2001 From: Jared Zhao Date: Mon, 16 Nov 2020 02:32:25 -0800 Subject: [PATCH 045/114] Updated temporal detection and tests (#139) * Updated temporal detection and tests * Reformatted code with black * Update PandasExecutor.py * added stock date test Co-authored-by: Doris Lee --- lux/executor/PandasExecutor.py | 35 ++++++++++++++++++++++++----- tests/test_type.py | 40 +++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/lux/executor/PandasExecutor.py b/lux/executor/PandasExecutor.py index f396c86b..806f47ba 100644 --- a/lux/executor/PandasExecutor.py +++ b/lux/executor/PandasExecutor.py @@ -383,12 +383,16 @@ def compute_dataset_metadata(self, ldf: LuxDataFrame): self.compute_data_model(ldf) def compute_data_type(self, ldf: LuxDataFrame): + from pandas.api.types import is_datetime64_any_dtype as is_datetime + for attr in list(ldf.columns): temporal_var_list = ["month", "year", "day", "date", "time"] - if isinstance(attr, pd._libs.tslibs.timestamps.Timestamp): - # If timestamp, make the dictionary keys the _repr_ (e.g., TimeStamp('2020-04-05 00.000')--> '2020-04-05') + if is_datetime(ldf[attr]): + ldf.data_type_lookup[attr] = "temporal" + elif self._is_datetime_string(ldf[attr]): + ldf.data_type_lookup[attr] = "temporal" + elif isinstance(attr, pd._libs.tslibs.timestamps.Timestamp): ldf.data_type_lookup[attr] = "temporal" - # elif any(var in str(attr).lower() for var in temporal_var_list): elif str(attr).lower() in temporal_var_list: ldf.data_type_lookup[attr] = "temporal" elif pd.api.types.is_float_dtype(ldf.dtypes[attr]): @@ -425,8 +429,6 @@ def compute_data_type(self, ldf: LuxDataFrame): ldf.data_type_lookup[ldf.index.name] = "nominal" ldf.data_type = self.mapping(ldf.data_type_lookup) - from pandas.api.types import is_datetime64_any_dtype as is_datetime - non_datetime_attrs = [] for attr in ldf.columns: if ldf.data_type_lookup[attr] == "temporal" and not is_datetime(ldf[attr]): @@ -450,6 +452,29 @@ def compute_data_type(self, ldf: LuxDataFrame): stacklevel=2, ) + def _is_datetime_string(self, series): + if len(series) > 100: + series = series.sample(100) + + if series.dtype == object: + + not_numeric = False + try: + pd.to_numeric(series) + except Exception as e: + not_numeric = True + + datetime_col = None + if not_numeric: + try: + datetime_col = pd.to_datetime(series) + except Exception as e: + return False + + if datetime_col is not None: + return True + return False + def compute_data_model(self, ldf: LuxDataFrame): ldf.data_model = { "measure": ldf.data_type["quantitative"], diff --git a/tests/test_type.py b/tests/test_type.py index f71766c0..0738d8bd 100644 --- a/tests/test_type.py +++ b/tests/test_type.py @@ -97,13 +97,51 @@ def test_check_airbnb(): "price": "quantitative", "minimum_nights": "quantitative", "number_of_reviews": "quantitative", - "last_review": "nominal", + "last_review": "temporal", "reviews_per_month": "quantitative", "calculated_host_listings_count": "quantitative", "availability_365": "quantitative", } +def test_check_datetime(): + df = pd.DataFrame( + { + "a": ["2020-01-01"], + "b": ["20-01-01"], + "c": ["20-jan-01"], + "d": ["20-january-01"], + "e": ["2020 January 01"], + "f": ["2020 January 01 00:00:00 pm PT"], + "g": ["2020 January 01 13:00:00"], + "h": ["2020 January 01 23:59:59 GTC-6"], + } + ) + df.maintain_metadata() + assert df.data_type_lookup == { + "a": "temporal", + "b": "temporal", + "c": "temporal", + "d": "temporal", + "e": "temporal", + "f": "temporal", + "g": "temporal", + "h": "temporal", + } + + +def test_check_stock(): + df = pd.read_csv( + "https://github.com/lux-org/lux-datasets/blob/master/data/stocks.csv?raw=true" + ) + df.maintain_metadata() + assert df.data_type_lookup == { + "symbol": "nominal", + "monthdate": "temporal", + "price": "quantitative", + }, "Stock dataset type detection error" + + def test_check_college(): df = pd.read_csv("lux/data/college.csv") df.maintain_metadata() From ea208337bacdfa612a5473dc4bbf33033fa40eb0 Mon Sep 17 00:00:00 2001 From: jinimukh <46768380+jinimukh@users.noreply.github.com> Date: Mon, 16 Nov 2020 05:26:37 -0800 Subject: [PATCH 046/114] Fix Inline comments breaking to new lines (#137) * add black to travis * reformat all code and adjust test * remove .idea * fix contributing doc * small change in contributing * update * reformat, update command to fix version * remove dev dependencies * first pass -- inline comments * _config/config.py * delete test notebook * action * line length 105 * executor * interestingness * processor * vislib * tests, travis, CONTRIBUTING * .format () changed * replace tabs with escape chars * update using black * more rewrites and merges into single line Co-authored-by: Doris Lee --- .travis.yml | 2 +- CONTRIBUTING.md | 2 +- lux/_config/config.py | 4 +- lux/action/column_group.py | 10 ++- lux/action/correlation.py | 8 +-- lux/action/custom.py | 8 +-- lux/action/enhance.py | 13 ++-- lux/action/filter.py | 8 +-- lux/action/generalize.py | 8 +-- lux/action/similarity.py | 10 +-- lux/action/univariate.py | 10 ++- lux/core/frame.py | 77 +++++++-------------- lux/executor/PandasExecutor.py | 72 +++++++------------ lux/executor/SQLExecutor.py | 69 +++--------------- lux/interestingness/interestingness.py | 33 +++------ lux/processor/Compiler.py | 96 ++++++++++---------------- lux/processor/Parser.py | 14 ++-- lux/processor/Validator.py | 18 ++--- lux/utils/date_utils.py | 26 +++---- lux/utils/message.py | 4 +- lux/utils/utils.py | 9 +-- lux/vis/Vis.py | 37 ++++------ lux/vis/VisList.py | 45 ++++-------- lux/vislib/altair/AltairChart.py | 22 +++--- lux/vislib/altair/AltairRenderer.py | 17 ++--- lux/vislib/altair/BarChart.py | 30 ++++---- lux/vislib/altair/Heatmap.py | 5 +- lux/vislib/altair/Histogram.py | 5 +- lux/vislib/altair/LineChart.py | 20 +++--- lux/vislib/altair/ScatterChart.py | 5 +- tests/test_action.py | 12 +--- tests/test_compiler.py | 43 +++++------- tests/test_dates.py | 25 ++----- tests/test_error_warning.py | 4 +- tests/test_executor.py | 50 ++++---------- tests/test_interestingness.py | 25 +++---- tests/test_maintainence.py | 15 ++-- tests/test_pandas_coverage.py | 58 ++++------------ tests/test_performance.py | 3 +- tests/test_type.py | 18 ++--- tests/test_vis.py | 54 ++++----------- 41 files changed, 307 insertions(+), 687 deletions(-) diff --git a/.travis.yml b/.travis.yml index 98bde1cf..6dfca243 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ install: - pip install git+https://github.com/lux-org/lux-widget # command to run tests script: - - black --target-version py37 --check . + - black --target-version py37 --line-length 105 --check . - python -m pytest tests/*.py - pytest --cov-report term --cov=lux tests/ after_success: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac05767b..a241410a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,7 @@ python -m pytest tests/*.py ``` # Code Formatting -In order to keep our codebase clean and readible, we are using PEP8 guidelines. To help us maintain and check code style, we are using [black](https://github.com/psf/black). Simply run `black .` before commiting. Failure to do so may fail the tests run on Travis. This package should have been installed for you. +In order to keep our codebase clean and readible, we are using PEP8 guidelines. To help us maintain and check code style, we are using [black](https://github.com/psf/black). Simply run `black --line-length 105 .` before commiting. Failure to do so may fail the tests run on Travis. This package should have been installed for you. # Submitting a Pull Request diff --git a/lux/_config/config.py b/lux/_config/config.py index 0c1e967f..809d89b1 100644 --- a/lux/_config/config.py +++ b/lux/_config/config.py @@ -115,9 +115,7 @@ def register_action( update_actions["flag"] = True -def remove_action( - name: str = "", -) -> None: +def remove_action(name: str = "") -> None: """ Removes the provided action globally in lux diff --git a/lux/action/column_group.py b/lux/action/column_group.py index 710cea95..29d33d92 100644 --- a/lux/action/column_group.py +++ b/lux/action/column_group.py @@ -31,9 +31,9 @@ def column_group(ldf): ldf_flat = ldf if isinstance(ldf.columns, pd.DatetimeIndex): ldf_flat.columns = ldf_flat.columns.format() - ldf_flat = ( - ldf_flat.reset_index() - ) # use a single shared ldf_flat so that metadata doesn't need to be computed for every vis + + # use a single shared ldf_flat so that metadata doesn't need to be computed for every vis + ldf_flat = ldf_flat.reset_index() if ldf.index.nlevels == 1: if ldf.index.name: index_column_name = ldf.index.name @@ -51,9 +51,7 @@ def column_group(ldf): data_model="dimension", aggregation=None, ), - lux.Clause( - str(attribute), data_type="quantitative", aggregation=None - ), + lux.Clause(str(attribute), data_type="quantitative", aggregation=None), ] ) collection.append(vis) diff --git a/lux/action/correlation.py b/lux/action/correlation.py index 5d51ba01..53cc8540 100644 --- a/lux/action/correlation.py +++ b/lux/action/correlation.py @@ -53,9 +53,8 @@ def correlation(ldf: LuxDataFrame, ignore_transpose: bool = True): "description": "Show relationships between two

quantitative

attributes.", } ignore_rec_flag = False - if ( - len(ldf) < 5 - ): # Doesn't make sense to compute correlation if less than 4 data values + # Doesn't make sense to compute correlation if less than 4 data values + if len(ldf) < 5: ignore_rec_flag = True # Then use the data populated in the vis list to compute score for vis in vlist: @@ -86,8 +85,7 @@ def correlation(ldf: LuxDataFrame, ignore_transpose: bool = True): def check_transpose_not_computed(vlist: VisList, a: str, b: str): transpose_exist = list( filter( - lambda x: (x._inferred_intent[0].attribute == b) - and (x._inferred_intent[1].attribute == a), + lambda x: (x._inferred_intent[0].attribute == b) and (x._inferred_intent[1].attribute == a), vlist, ) ) diff --git a/lux/action/custom.py b/lux/action/custom.py index c709d34b..72ece683 100644 --- a/lux/action/custom.py +++ b/lux/action/custom.py @@ -67,14 +67,10 @@ def custom_actions(ldf): recommendations = [] for action_name in lux.actions.__dir__(): display_condition = lux.actions.__getattr__(action_name).display_condition - if display_condition is None or ( - display_condition is not None and display_condition(ldf) - ): + 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).action( - ldf, args - ) + recommendation = lux.actions.__getattr__(action_name).action(ldf, args) else: recommendation = lux.actions.__getattr__(action_name).action(ldf) recommendations.append(recommendation) diff --git a/lux/action/enhance.py b/lux/action/enhance.py index ffdc2423..a74bd452 100644 --- a/lux/action/enhance.py +++ b/lux/action/enhance.py @@ -35,14 +35,10 @@ def enhance(ldf): filters = utils.get_filter_specs(ldf._intent) # Collect variables that already exist in the intent - attr_specs = list( - filter(lambda x: x.value == "" and x.attribute != "Record", ldf._intent) - ) + attr_specs = list(filter(lambda x: x.value == "" and x.attribute != "Record", ldf._intent)) fltr_str = [fltr.attribute + fltr.filter_op + str(fltr.value) for fltr in filters] attr_str = [clause.attribute for clause in attr_specs] - intended_attrs = ( - '

' + ", ".join(attr_str + fltr_str) + "

" - ) + intended_attrs = '

' + ", ".join(attr_str + fltr_str) + "

" if len(attr_specs) == 1: recommendation = { "action": "Enhance", @@ -53,9 +49,8 @@ def enhance(ldf): "action": "Enhance", "description": f"Further breaking down current {intended_attrs} intent by additional attribute.", } - elif ( - len(attr_specs) > 2 - ): # if there are too many column attributes, return don't generate Enhance recommendations + # if there are too many column attributes, return don't generate Enhance recommendations + elif len(attr_specs) > 2: recommendation = {"action": "Enhance"} recommendation["collection"] = [] return recommendation diff --git a/lux/action/filter.py b/lux/action/filter.py index f0972722..891ad909 100644 --- a/lux/action/filter.py +++ b/lux/action/filter.py @@ -86,8 +86,8 @@ def get_complementary_ops(fltr_op): new_spec.append(new_filter) temp_vis = Vis(new_spec, score=1) output.append(temp_vis) - - else: # if no existing filters, create filters using unique values from all categorical variables in the dataset + # if no existing filters, create filters using unique values from all categorical variables in the dataset + else: intended_attrs = ", ".join( [ clause.attribute @@ -108,9 +108,7 @@ def get_complementary_ops(fltr_op): unique_values = ldf.unique_values[cat] for i in range(0, len(unique_values)): new_spec = column_spec.copy() - new_filter = lux.Clause( - attribute=cat, filter_op="=", value=unique_values[i] - ) + new_filter = lux.Clause(attribute=cat, filter_op="=", value=unique_values[i]) new_spec.append(new_filter) temp_vis = Vis(new_spec) output.append(temp_vis) diff --git a/lux/action/generalize.py b/lux/action/generalize.py index c6096cc0..d95bcb26 100644 --- a/lux/action/generalize.py +++ b/lux/action/generalize.py @@ -38,16 +38,12 @@ def generalize(ldf): output = [] excluded_columns = [] - attributes = list( - filter(lambda x: x.value == "" and x.attribute != "Record", ldf._intent) - ) + attributes = list(filter(lambda x: x.value == "" and x.attribute != "Record", ldf._intent)) filters = utils.get_filter_specs(ldf._intent) fltr_str = [fltr.attribute + fltr.filter_op + str(fltr.value) for fltr in filters] attr_str = [clause.attribute for clause in attributes] - intended_attrs = ( - '

' + ", ".join(attr_str + fltr_str) + "

" - ) + intended_attrs = '

' + ", ".join(attr_str + fltr_str) + "

" recommendation = { "action": "Generalize", diff --git a/lux/action/similarity.py b/lux/action/similarity.py index c9871cbc..174a4d43 100644 --- a/lux/action/similarity.py +++ b/lux/action/similarity.py @@ -80,12 +80,7 @@ def aggregate(vis): xAxis = vis.get_attr_by_channel("x")[0].attribute yAxis = vis.get_attr_by_channel("y")[0].attribute - vis.data = ( - vis.data[[xAxis, yAxis]] - .groupby(xAxis, as_index=False) - .agg({yAxis: "mean"}) - .copy() - ) + vis.data = vis.data[[xAxis, yAxis]].groupby(xAxis, as_index=False).agg({yAxis: "mean"}).copy() def interpolate(vis, length): @@ -133,8 +128,7 @@ def interpolate(vis, length): x_diff = xVals[count] - xVals[count - 1] yDiff = yVals[count] - yVals[count - 1] interpolated_y_vals[i] = ( - yVals[count - 1] - + (interpolated_x - xVals[count - 1]) / x_diff * yDiff + yVals[count - 1] + (interpolated_x - xVals[count - 1]) / x_diff * yDiff ) vis.data = pd.DataFrame( list(zip(interpolated_x_vals, interpolated_y_vals)), diff --git a/lux/action/univariate.py b/lux/action/univariate.py index 4eb0157e..8f8cd1ac 100644 --- a/lux/action/univariate.py +++ b/lux/action/univariate.py @@ -58,9 +58,8 @@ def univariate(ldf, *args): "action": "Distribution", "description": "Show univariate histograms of

quantitative

attributes.", } - if ( - len(ldf) < 5 - ): # Doesn't make sense to generate a histogram if there is less than 5 datapoints (pre-aggregated) + # Doesn't make sense to generate a histogram if there is less than 5 datapoints (pre-aggregated) + if len(ldf) < 5: ignore_rec_flag = True elif data_type_constraint == "nominal": intent = [lux.Clause("?", data_type="nominal")] @@ -76,9 +75,8 @@ def univariate(ldf, *args): "action": "Temporal", "description": "Show trends over

time-related

attributes.", } - if ( - len(ldf) < 3 - ): # Doesn't make sense to generate a line chart if there is less than 3 datapoints (pre-aggregated) + # Doesn't make sense to generate a line chart if there is less than 3 datapoints (pre-aggregated) + if len(ldf) < 3: ignore_rec_flag = True if ignore_rec_flag: recommendation["collection"] = [] diff --git a/lux/core/frame.py b/lux/core/frame.py index 6e45621b..3c6b3977 100644 --- a/lux/core/frame.py +++ b/lux/core/frame.py @@ -102,12 +102,10 @@ def history(self): return self._history def maintain_metadata(self): - if ( - not hasattr(self, "_metadata_fresh") or not self._metadata_fresh - ): # Check that metadata has not yet been computed - if ( - len(self) > 0 - ): # only compute metadata information if the dataframe is non-empty + # Check that metadata has not yet been computed + if not hasattr(self, "_metadata_fresh") or not self._metadata_fresh: + # only compute metadata information if the dataframe is non-empty + if len(self) > 0: self.executor.compute_stats(self) self.executor.compute_dataset_metadata(self) self._infer_structure() @@ -162,9 +160,7 @@ def _infer_structure(self): is_multi_index_flag = self.index.nlevels != 1 not_int_index_flag = self.index.dtype != "int64" small_df_flag = len(self) < 100 - self.pre_aggregated = ( - is_multi_index_flag or not_int_index_flag - ) and small_df_flag + self.pre_aggregated = (is_multi_index_flag or not_int_index_flag) and small_df_flag if "Number of Records" in self.columns: self.pre_aggregated = True very_small_df_flag = len(self) <= 10 @@ -368,10 +364,8 @@ def get_SQL_attributes(self): table_name = self.table_name[self.table_name.index(".") + 1 :] else: table_name = self.table_name - attr_query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '{}'".format( - table_name - ) - attributes = list(pd.read_sql(attr_query, self.SQLconnection)["column_name"]) + query = f"SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '{table_name}'" + attributes = list(pd.read_sql(query, self.SQLconnection)["column_name"]) for attr in attributes: self[attr] = None @@ -379,7 +373,7 @@ def get_SQL_cardinality(self): cardinality = {} for attr in list(self.columns): card_query = pd.read_sql( - "SELECT Count(Distinct({})) FROM {}".format(attr, self.table_name), + f"SELECT Count(Distinct({attr})) FROM {self.table_name}", self.SQLconnection, ) cardinality[attr] = list(card_query["count"])[0] @@ -389,7 +383,7 @@ def get_SQL_unique_values(self): unique_vals = {} for attr in list(self.columns): unique_query = pd.read_sql( - "SELECT Distinct({}) FROM {}".format(attr, self.table_name), + f"SELECT Distinct({attr}) FROM {self.table_name}", self.SQLconnection, ) unique_vals[attr] = list(unique_query[attr]) @@ -405,12 +399,8 @@ def compute_SQL_data_type(self): table_name = self.table_name # get the data types of the attributes in the SQL table for attr in list(self.columns): - datatype_query = "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{}' AND COLUMN_NAME = '{}'".format( - table_name, attr - ) - datatype = list( - pd.read_sql(datatype_query, self.SQLconnection)["data_type"] - )[0] + query = f"SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{table_name}' AND COLUMN_NAME = '{attr}'" + datatype = list(pd.read_sql(query, self.SQLconnection)["data_type"])[0] sql_dtypes[attr] = datatype data_type = {"quantitative": [], "nominal": [], "temporal": []} @@ -447,10 +437,7 @@ def compute_SQL_data_type(self): self.data_type = data_type def _append_rec(self, rec_infolist, recommendations: Dict): - if ( - recommendations["collection"] is not None - and len(recommendations["collection"]) > 0 - ): + if recommendations["collection"] is not None and len(recommendations["collection"]) > 0: rec_infolist.append(recommendations) def maintain_recs(self): @@ -477,14 +464,11 @@ def maintain_recs(self): 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.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 + # Check that recs has not yet been computed + if not hasattr(rec_df, "_recs_fresh") or not rec_df._recs_fresh: rec_infolist = [] from lux.action.custom import custom from lux.action.custom import custom_actions @@ -507,19 +491,15 @@ def maintain_recs(self): 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 + 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 + 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("distribution", univariate, no_vis, "quantitative") lux.register_action("occurrence", univariate, no_vis, "nominal") lux.register_action("temporal", univariate, no_vis, "temporal") @@ -550,9 +530,8 @@ def maintain_recs(self): rec_df.recommendation[action_type] = vlist rec_df._rec_info = rec_infolist self._widget = rec_df.render_widget() - elif ( - show_prev - ): # re-render widget for the current dataframe if previous rec is not recomputed + # re-render widget for the current dataframe if previous rec is not recomputed + elif show_prev: self._widget = rec_df.render_widget() self._recs_fresh = True @@ -651,9 +630,7 @@ def set_intent_on_click(self, change): from lux.processor.Compiler import Compiler intent_action = list(self._widget.selectedIntentIndex.keys())[0] - vis = self.recommendation[intent_action][ - self._widget.selectedIntentIndex[intent_action][0] - ] + vis = self.recommendation[intent_action][self._widget.selectedIntentIndex[intent_action][0]] self.set_intent_as_vis(vis) self.maintain_metadata() @@ -704,9 +681,7 @@ def _repr_html_(self): return self.maintain_metadata() - if self._intent != [] and ( - not hasattr(self, "_compiled") or not self._compiled - ): + if self._intent != [] and (not hasattr(self, "_compiled") or not self._compiled): from lux.processor.Compiler import Compiler self.current_vis = Compiler.compile_intent(self, self._intent) @@ -721,9 +696,7 @@ def _repr_html_(self): # Observers(callback_function, listen_to_this_variable) self._widget.observe(self.remove_deleted_recs, names="deletedIndices") - self._widget.observe( - self.set_intent_on_click, names="selectedIntentIndex" - ) + self._widget.observe(self.set_intent_on_click, names="selectedIntentIndex") if len(self.recommendation) > 0: # box = widgets.Box(layout=widgets.Layout(display='inline')) @@ -740,9 +713,7 @@ def _repr_html_(self): def on_button_clicked(b): with self.output: if b: - self._toggle_pandas_display = ( - not self._toggle_pandas_display - ) + self._toggle_pandas_display = not self._toggle_pandas_display clear_output() if self._toggle_pandas_display: display(self.display_pandas()) diff --git a/lux/executor/PandasExecutor.py b/lux/executor/PandasExecutor.py index 806f47ba..a73e607b 100644 --- a/lux/executor/PandasExecutor.py +++ b/lux/executor/PandasExecutor.py @@ -80,9 +80,8 @@ def execute(vislist: VisList, ldf: LuxDataFrame): """ PandasExecutor.execute_sampling(ldf) for vis in vislist: - vis._vis_data = ( - ldf._sampled - ) # The vis data starts off being original or sampled dataframe + # The vis data starts off being original or sampled dataframe + vis._vis_data = ldf._sampled filter_executed = PandasExecutor.execute_filter(vis) # Select relevant data based on attribute information attributes = set([]) @@ -163,21 +162,15 @@ def execute_aggregate(vis: Vis, isFiltered=True): .reset_index() ) vis._vis_data = vis.data.rename(columns={"index": "Record"}) - vis._vis_data = vis.data[ - [groupby_attr.attribute, color_attr.attribute, "Record"] - ] + vis._vis_data = vis.data[[groupby_attr.attribute, color_attr.attribute, "Record"]] else: - vis._vis_data = ( - vis.data.groupby(groupby_attr.attribute).count().reset_index() - ) + vis._vis_data = vis.data.groupby(groupby_attr.attribute).count().reset_index() vis._vis_data = vis.data.rename(columns={"index": "Record"}) vis._vis_data = vis.data[[groupby_attr.attribute, "Record"]] else: # if color is specified, need to group by groupby_attr and color_attr if has_color: - groupby_result = vis.data.groupby( - [groupby_attr.attribute, color_attr.attribute] - ) + groupby_result = vis.data.groupby([groupby_attr.attribute, color_attr.attribute]) else: groupby_result = vis.data.groupby(groupby_attr.attribute) groupby_result = groupby_result.agg(agg_func) @@ -200,9 +193,7 @@ def execute_aggregate(vis: Vis, isFiltered=True): df = pd.DataFrame( { columns[0]: attr_unique_vals * color_cardinality, - columns[1]: pd.Series(color_attr_vals).repeat( - N_unique_vals - ), + columns[1]: pd.Series(color_attr_vals).repeat(N_unique_vals), } ) vis._vis_data = vis.data.merge( @@ -212,17 +203,14 @@ def execute_aggregate(vis: Vis, isFiltered=True): suffixes=["", "_right"], ) for col in columns[2:]: - vis.data[col] = vis.data[col].fillna( - 0 - ) # Triggers __setitem__ - assert len( - list(vis.data[groupby_attr.attribute]) - ) == N_unique_vals * len( + vis.data[col] = vis.data[col].fillna(0) # Triggers __setitem__ + assert len(list(vis.data[groupby_attr.attribute])) == N_unique_vals * len( color_attr_vals ), f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute, color_attr.attribute}`." - vis._vis_data = vis.data.iloc[ - :, :3 - ] # Keep only the three relevant columns not the *_right columns resulting from merge + + # Keep only the three relevant columns not the *_right columns resulting from merge + vis._vis_data = vis.data.iloc[:, :3] + else: df = pd.DataFrame({columns[0]: attr_unique_vals}) @@ -235,9 +223,7 @@ def execute_aggregate(vis: Vis, isFiltered=True): assert ( len(list(vis.data[groupby_attr.attribute])) == N_unique_vals ), f"Aggregated data missing values compared to original range of values of `{groupby_attr.attribute}`." - vis._vis_data = vis.data.sort_values( - by=groupby_attr.attribute, ascending=True - ) + vis._vis_data = vis.data.sort_values(by=groupby_attr.attribute, ascending=True) vis._vis_data = vis.data.reset_index() vis._vis_data = vis.data.drop(columns="index") @@ -260,19 +246,17 @@ def execute_binning(vis: Vis): import numpy as np bin_attribute = list(filter(lambda x: x.bin_size != 0, vis._inferred_intent))[0] - if not np.isnan(vis.data[bin_attribute.attribute]).all(): - series = vis.data[ - bin_attribute.attribute - ].dropna() # np.histogram breaks if array contain NaN + bin_attr = bin_attribute.attribute + if not np.isnan(vis.data[bin_attr]).all(): + # np.histogram breaks if array contain NaN + series = vis.data[bin_attr].dropna() # TODO:binning runs for name attribte. Name attribute has datatype quantitative which is wrong. counts, bin_edges = np.histogram(series, bins=bin_attribute.bin_size) # bin_edges of size N+1, so need to compute bin_center as the bin location bin_center = np.mean(np.vstack([bin_edges[0:-1], bin_edges[1:]]), axis=0) # TODO: Should vis.data be a LuxDataFrame or a Pandas DataFrame? - vis._vis_data = pd.DataFrame( - np.array([bin_center, counts]).T, - columns=[bin_attribute.attribute, "Number of Records"], - ) + binned_result = np.array([bin_center, counts]).T + vis._vis_data = pd.DataFrame(binned_result, columns=[bin_attr, "Number of Records"]) @staticmethod def execute_filter(vis: Vis): @@ -292,9 +276,7 @@ def execute_filter(vis: Vis): return False @staticmethod - def apply_filter( - df: pd.DataFrame, attribute: str, op: str, val: object - ) -> pd.DataFrame: + def apply_filter(df: pd.DataFrame, attribute: str, op: str, val: object) -> pd.DataFrame: """ Helper function for applying filter to a dataframe @@ -402,10 +384,7 @@ def compute_data_type(self, ldf: LuxDataFrame): if ldf.pre_aggregated: if ldf.cardinality[attr] == len(ldf): ldf.data_type_lookup[attr] = "nominal" - if ( - ldf.cardinality[attr] / len(ldf) < 0.4 - and ldf.cardinality[attr] < 20 - ): + if ldf.cardinality[attr] / len(ldf) < 0.4 and ldf.cardinality[attr] < 20: ldf.data_type_lookup[attr] = "nominal" else: ldf.data_type_lookup[attr] = "quantitative" @@ -417,9 +396,8 @@ def compute_data_type(self, ldf: LuxDataFrame): ldf.data_type_lookup[attr] = "id" else: ldf.data_type_lookup[attr] = "nominal" - elif is_datetime_series( - ldf.dtypes[attr] - ): # check if attribute is any type of datetime dtype + # check if attribute is any type of datetime dtype + elif is_datetime_series(ldf.dtypes[attr]): ldf.data_type_lookup[attr] = "temporal" else: ldf.data_type_lookup[attr] = "nominal" @@ -478,9 +456,7 @@ def _is_datetime_string(self, series): def compute_data_model(self, ldf: LuxDataFrame): ldf.data_model = { "measure": ldf.data_type["quantitative"], - "dimension": ldf.data_type["nominal"] - + ldf.data_type["temporal"] - + ldf.data_type["id"], + "dimension": ldf.data_type["nominal"] + ldf.data_type["temporal"] + ldf.data_type["id"], } ldf.data_model_lookup = self.reverseMapping(ldf.data_model) diff --git a/lux/executor/SQLExecutor.py b/lux/executor/SQLExecutor.py index 2cca392d..05c608d1 100644 --- a/lux/executor/SQLExecutor.py +++ b/lux/executor/SQLExecutor.py @@ -60,20 +60,14 @@ def execute(vislist: VisList, ldf: LuxDataFrame): required_variables = ",".join(required_variables) row_count = list( pd.read_sql( - "SELECT COUNT(*) FROM {} {}".format( - ldf.table_name, where_clause - ), + f"SELECT COUNT(*) FROM {ldf.table_name} {where_clause}", ldf.SQLconnection, )["count"] )[0] if row_count > 10000: - query = "SELECT {} FROM {} {} ORDER BY random() LIMIT 10000".format( - required_variables, ldf.table_name, where_clause - ) + query = f"SELECT {required_variables} FROM {ldf.table_name} {where_clause} ORDER BY random() LIMIT 10000" else: - query = "SELECT {} FROM {} {}".format( - required_variables, ldf.table_name, where_clause - ) + query = f"SELECT {required_variables} FROM {ldf.table_name} {where_clause}" data = pd.read_sql(query, ldf.SQLconnection) vis._vis_data = utils.pandas_to_lux(data) if vis.mark == "bar" or vis.mark == "line": @@ -102,13 +96,7 @@ def execute_aggregate(vis: Vis, ldf: LuxDataFrame): # barchart case, need count data for each group if measure_attr.attribute == "Record": where_clause, filterVars = SQLExecutor.execute_filter(vis) - count_query = "SELECT {}, COUNT({}) FROM {} {} GROUP BY {}".format( - groupby_attr.attribute, - groupby_attr.attribute, - ldf.table_name, - where_clause, - groupby_attr.attribute, - ) + count_query = f"SELECT {groupby_attr.attribute}, COUNT({groupby_attr.attribute}) FROM {ldf.table_name} {where_clause} GROUP BY {groupby_attr.attribute}" vis._vis_data = pd.read_sql(count_query, ldf.SQLconnection) vis._vis_data = vis.data.rename(columns={"count": "Record"}) vis._vis_data = utils.pandas_to_lux(vis.data) @@ -116,42 +104,15 @@ def execute_aggregate(vis: Vis, ldf: LuxDataFrame): else: where_clause, filterVars = SQLExecutor.execute_filter(vis) if agg_func == "mean": - mean_query = ( - "SELECT {}, AVG({}) as {} FROM {} {} GROUP BY {}".format( - groupby_attr.attribute, - measure_attr.attribute, - measure_attr.attribute, - ldf.table_name, - where_clause, - groupby_attr.attribute, - ) - ) + mean_query = f"SELECT {groupby_attr.attribute}, AVG({measure_attr.attribute}) as {measure_attr.attribute} FROM {ldf.table_name} {where_clause} GROUP BY {groupby_attr.attribute}" vis._vis_data = pd.read_sql(mean_query, ldf.SQLconnection) vis._vis_data = utils.pandas_to_lux(vis.data) if agg_func == "sum": - mean_query = ( - "SELECT {}, SUM({}) as {} FROM {} {} GROUP BY {}".format( - groupby_attr.attribute, - measure_attr.attribute, - measure_attr.attribute, - ldf.table_name, - where_clause, - groupby_attr.attribute, - ) - ) + mean_query = f"SELECT {groupby_attr.attribute}, SUM({measure_attr.attribute}) as {measure_attr.attribute} FROM {ldf.table_name} {where_clause} GROUP BY {groupby_attr.attribute}" vis._vis_data = pd.read_sql(mean_query, ldf.SQLconnection) vis._vis_data = utils.pandas_to_lux(vis.data) if agg_func == "max": - mean_query = ( - "SELECT {}, MAX({}) as {} FROM {} {} GROUP BY {}".format( - groupby_attr.attribute, - measure_attr.attribute, - measure_attr.attribute, - ldf.table_name, - where_clause, - groupby_attr.attribute, - ) - ) + mean_query = f"SELECT {groupby_attr.attribute}, MAX({measure_attr.attribute}) as {measure_attr.attribute} FROM {ldf.table_name} {where_clause} GROUP BY {groupby_attr.attribute}" vis._vis_data = pd.read_sql(mean_query, ldf.SQLconnection) vis._vis_data = utils.pandas_to_lux(vis.data) @@ -162,9 +123,7 @@ def execute_aggregate(vis: Vis, ldf: LuxDataFrame): # For filtered aggregation that have missing groupby-attribute values, set these aggregated value as 0, since no datapoints for vals in all_attr_vals: if vals not in result_vals: - vis.data.loc[len(vis.data)] = [vals] + [0] * ( - len(vis.data.columns) - 1 - ) + vis.data.loc[len(vis.data)] = [vals] + [0] * (len(vis.data.columns) - 1) @staticmethod def execute_binning(vis: Vis, ldf: LuxDataFrame): @@ -191,18 +150,14 @@ def execute_binning(vis: Vis, ldf: LuxDataFrame): upper_edges.append(str(curr_edge)) upper_edges = ",".join(upper_edges) vis_filter, filter_vars = SQLExecutor.execute_filter(vis) - bin_count_query = "SELECT width_bucket, COUNT(width_bucket) FROM (SELECT width_bucket({}, '{}') FROM {}) as Buckets GROUP BY width_bucket ORDER BY width_bucket".format( - bin_attribute.attribute, "{" + upper_edges + "}", ldf.table_name - ) + bin_count_query = f"SELECT width_bucket, COUNT(width_bucket) FROM (SELECT width_bucket({bin_attribute.attribute}, '{{{upper_edges}}}') FROM {ldf.table_name}) as Buckets GROUP BY width_bucket ORDER BY width_bucket" bin_count_data = pd.read_sql(bin_count_query, ldf.SQLconnection) # counts,binEdges = np.histogram(ldf[bin_attribute.attribute],bins=bin_attribute.bin_size) # binEdges of size N+1, so need to compute binCenter as the bin location upper_edges = [float(i) for i in upper_edges.split(",")] if attr_type == int: - bin_centers = np.array( - [math.ceil((attr_min + attr_min + bin_width) / 2)] - ) + bin_centers = np.array([math.ceil((attr_min + attr_min + bin_width) / 2)]) else: bin_centers = np.array([(attr_min + attr_min + bin_width) / 2]) bin_centers = np.append( @@ -215,9 +170,7 @@ def execute_binning(vis: Vis, ldf: LuxDataFrame): math.ceil((upper_edges[len(upper_edges) - 1] + attr_max) / 2), ) else: - bin_centers = np.append( - bin_centers, (upper_edges[len(upper_edges) - 1] + attr_max) / 2 - ) + bin_centers = np.append(bin_centers, (upper_edges[len(upper_edges) - 1] + attr_max) / 2) if len(bin_centers) > len(bin_count_data): bucket_lables = bin_count_data["width_bucket"].unique() diff --git a/lux/interestingness/interestingness.py b/lux/interestingness/interestingness.py index 9d175583..bc6fcbb3 100644 --- a/lux/interestingness/interestingness.py +++ b/lux/interestingness/interestingness.py @@ -75,9 +75,7 @@ def interestingness(vis: Vis, ldf: LuxDataFrame) -> int: if n_filter == 0: return unevenness(vis, ldf, measure_lst, dimension_lst) elif n_filter == 1: - return deviation_from_overall( - vis, ldf, filter_specs, measure_lst[0].attribute - ) + return deviation_from_overall(vis, ldf, filter_specs, measure_lst[0].attribute) # Histogram elif n_dim == 0 and n_msr == 1: if v_size < 2: @@ -94,9 +92,7 @@ def interestingness(vis: Vis, ldf: LuxDataFrame) -> int: if v_size < 10: return -1 if vis.mark == "heatmap": - return weighted_correlation( - vis.data["xBinStart"], vis.data["yBinStart"], vis.data["count"] - ) + return weighted_correlation(vis.data["xBinStart"], vis.data["yBinStart"], vis.data["count"]) if n_filter == 1: v_filter_size = get_filtered_size(filter_specs, vis.data) sig = v_filter_size / v_size @@ -139,9 +135,7 @@ def interestingness(vis: Vis, ldf: LuxDataFrame) -> int: groupby_unique_vals = ldf.unique_values[groupby_column] for c in range(0, groupby_cardinality): contingency_table.append( - vis.data[vis.data[groupby_column] == groupby_unique_vals[c]][ - measure_column - ] + vis.data[vis.data[groupby_column] == groupby_unique_vals[c]][measure_column] ) score = 0.12 # ValueError results if an entire column of the contingency table is 0, can happen if an applied filter results in @@ -186,14 +180,10 @@ def weighted_cov(x, y, w): def weighted_correlation(x, y, w): # Based on https://en.wikipedia.org/wiki/Pearson_correlation_coefficient#Weighted_correlation_coefficient - return weighted_cov(x, y, w) / np.sqrt( - weighted_cov(x, x, w) * weighted_cov(y, y, w) - ) + return weighted_cov(x, y, w) / np.sqrt(weighted_cov(x, x, w) * weighted_cov(y, y, w)) -def deviation_from_overall( - vis: Vis, ldf: LuxDataFrame, filter_specs: list, msr_attribute: str -) -> int: +def deviation_from_overall(vis: Vis, ldf: LuxDataFrame, filter_specs: list, msr_attribute: str) -> int: """ Difference in bar chart/histogram shape from overall chart Note: this function assumes that the filtered vis.data is operating on the same range as the unfiltered vis.data. @@ -223,16 +213,13 @@ def deviation_from_overall( import copy unfiltered_vis = copy.copy(vis) - unfiltered_vis._inferred_intent = utils.get_attrs_specs( - vis._inferred_intent - ) # Remove filters, keep only attribute intent + # Remove filters, keep only attribute intent + unfiltered_vis._inferred_intent = utils.get_attrs_specs(vis._inferred_intent) ldf.executor.execute([unfiltered_vis], ldf) v = unfiltered_vis.data[msr_attribute] v = v / v.sum() - assert len(v) == len( - v_filter - ), "Data for filtered and unfiltered vis have unequal length." + assert len(v) == len(v_filter), "Data for filtered and unfiltered vis have unequal length." sig = v_filter_size / v_size # significance factor # Euclidean distance as L2 function @@ -257,9 +244,7 @@ def deviation_from_overall( return sig * rankSig * euclidean(v, v_filter) -def unevenness( - vis: Vis, ldf: LuxDataFrame, measure_lst: list, dimension_lst: list -) -> int: +def unevenness(vis: Vis, ldf: LuxDataFrame, measure_lst: list, dimension_lst: list) -> int: """ Measure the unevenness of a bar chart vis. If a bar chart is highly uneven across the possible values, then it may be interesting. (e.g., USA produces lots of cars compared to Japan and Europe) diff --git a/lux/processor/Compiler.py b/lux/processor/Compiler.py index 0635f2de..d5558f34 100644 --- a/lux/processor/Compiler.py +++ b/lux/processor/Compiler.py @@ -37,16 +37,13 @@ def __repr__(self): @staticmethod def compile_vis(ldf: LuxDataFrame, vis: Vis) -> VisList: if vis: - vis_collection = Compiler.populate_data_type_model( - ldf, [vis] - ) # autofill data type/model information - vis_collection = Compiler.remove_all_invalid( - vis_collection - ) # remove invalid visualizations from collection + # autofill data type/model information + vis_collection = Compiler.populate_data_type_model(ldf, [vis]) + # remove invalid visualizations from collection + vis_collection = Compiler.remove_all_invalid(vis_collection) for vis in vis_collection: - Compiler.determine_encoding( - ldf, vis - ) # autofill viz related information + # autofill viz related information + Compiler.determine_encoding(ldf, vis) ldf._compiled = True return vis_collection @@ -72,24 +69,19 @@ def compile_intent(ldf: LuxDataFrame, _inferred_intent: List[Clause]) -> VisList """ if _inferred_intent: vis_collection = Compiler.enumerate_collection(_inferred_intent, ldf) - vis_collection = Compiler.populate_data_type_model( - ldf, vis_collection - ) # autofill data type/model information + # autofill data type/model information + vis_collection = Compiler.populate_data_type_model(ldf, vis_collection) + # remove invalid visualizations from collection if len(vis_collection) >= 1: - vis_collection = Compiler.remove_all_invalid( - vis_collection - ) # remove invalid visualizations from collection + vis_collection = Compiler.remove_all_invalid(vis_collection) for vis in vis_collection: - Compiler.determine_encoding( - ldf, vis - ) # autofill viz related information + # autofill viz related information + Compiler.determine_encoding(ldf, vis) ldf._compiled = True return vis_collection @staticmethod - def enumerate_collection( - _inferred_intent: List[Clause], ldf: LuxDataFrame - ) -> VisList: + def enumerate_collection(_inferred_intent: List[Clause], ldf: LuxDataFrame) -> VisList: """ Given specifications that have been expanded thorught populateOptions, recursively iterate over the resulting list combinations to generate a vis list. @@ -121,9 +113,8 @@ def combine(col_attrs, accum): for i in range(n): column_list = copy.deepcopy(accum + [col_attrs[0][i]]) if last: - if ( - len(filters) > 0 - ): # if we have filters, generate combinations for each row. + # if we have filters, generate combinations for each row. + if len(filters) > 0: for row in filters: _inferred_intent = copy.deepcopy(column_list + [row]) vis = Vis(_inferred_intent) @@ -164,9 +155,8 @@ def populate_data_type_model(ldf, vis_collection) -> VisList: if clause.description == "?": clause.description = "" # TODO: Note that "and not is_datetime_string(clause.attribute))" is a temporary hack and breaks the `test_row_column_group` example - if ( - clause.attribute != "" and clause.attribute != "Record" - ): # and not is_datetime_string(clause.attribute): + # and not is_datetime_string(clause.attribute): + if clause.attribute != "" and clause.attribute != "Record": if clause.data_type == "": clause.data_type = ldf.data_type_lookup[clause.attribute] if clause.data_type == "id": @@ -174,16 +164,13 @@ def populate_data_type_model(ldf, vis_collection) -> VisList: if clause.data_model == "": clause.data_model = ldf.data_model_lookup[clause.attribute] if clause.value != "": - if ( - vis.title == "" - ): # If user provided title for Vis, then don't override. + # If user provided title for Vis, then don't override. + if vis.title == "": if isinstance(clause.value, np.datetime64): chart_title = date_utils.date_formatter(clause.value, ldf) else: chart_title = clause.value - vis.title = ( - f"{clause.attribute} {clause.filter_op} {chart_title}" - ) + vis.title = f"{clause.attribute} {clause.filter_op} {chart_title}" return vlist @staticmethod @@ -303,10 +290,9 @@ def line_or_bar(ldf, dimension: Clause, measure: Clause): dimension = d2 color_attr = d1 else: + # if same attribute then remove_column_from_spec will remove both dims, we only want to remove one if d1.attribute == d2.attribute: - vis._inferred_intent.pop( - 0 - ) # if same attribute then remove_column_from_spec will remove both dims, we only want to remove one + vis._inferred_intent.pop(0) else: vis.remove_column_from_spec(d2.attribute) dimension = d1 @@ -345,9 +331,7 @@ def line_or_bar(ldf, dimension: Clause, measure: Clause): "y": vis._inferred_intent[1], "color": vis._inferred_intent[2], } - relevant_attributes = [ - auto_channel[channel].attribute for channel in auto_channel - ] + relevant_attributes = [auto_channel[channel].attribute for channel in auto_channel] relevant_min_max = dict( (attr, ldf._min_max[attr]) for attr in relevant_attributes @@ -380,12 +364,10 @@ def enforce_specified_channel(vis: Vis, auto_channel: Dict[str, str]): ValueError Ensures no more than one attribute is placed in the same channel. """ - result_dict = ( - {} - ) # result of enforcing specified channel will be stored in result_dict - specified_dict = ( - {} - ) # specified_dict={"x":[],"y":[list of Dobj with y specified as channel]} + # result of enforcing specified channel will be stored in result_dict + result_dict = {} + # specified_dict={"x":[],"y":[list of Dobj with y specified as channel]} + specified_dict = {} # create a dictionary of specified channels in the given dobj for val in auto_channel.keys(): specified_dict[val] = vis.get_attr_by_channel(val) @@ -395,9 +377,11 @@ def enforce_specified_channel(vis: Vis, auto_channel: Dict[str, str]): if len(sAttr) == 1: # if specified in dobj # remove the specified channel from auto_channel (matching by value, since channel key may not be same) for i in list(auto_channel.keys()): - if (auto_channel[i].attribute == sAttr[0].attribute) and ( - auto_channel[i].channel == sVal - ): # need to ensure that the channel is the same (edge case when duplicate Cols with same attribute name) + # need to ensure that the channel is the same (edge case when duplicate Cols with same attribute name) + if ( + auto_channel[i].attribute == sAttr[0].attribute + and auto_channel[i].channel == sVal + ): auto_channel.pop(i) break sAttr[0].channel = sVal @@ -410,9 +394,7 @@ def enforce_specified_channel(vis: Vis, auto_channel: Dict[str, str]): # and the leftovers in the auto_channel specification, # step through them together and fill it automatically. leftover_channels = list(filter(lambda x: result_dict[x] == "", result_dict)) - for leftover_channel, leftover_encoding in zip( - leftover_channels, auto_channel.values() - ): + for leftover_channel, leftover_encoding in zip(leftover_channels, auto_channel.values()): leftover_encoding.channel = leftover_channel result_dict[leftover_channel] = leftover_encoding vis._inferred_intent = list(result_dict.values()) @@ -420,9 +402,7 @@ def enforce_specified_channel(vis: Vis, auto_channel: Dict[str, str]): @staticmethod # def populate_wildcard_options(ldf: LuxDataFrame) -> dict: - def populate_wildcard_options( - _inferred_intent: List[Clause], ldf: LuxDataFrame - ) -> dict: + def populate_wildcard_options(_inferred_intent: List[Clause], ldf: LuxDataFrame) -> dict: """ Given wildcards and constraints in the LuxDataFrame's intent, return the list of available values that satisfies the data_type or data_model constraints. @@ -447,13 +427,9 @@ def populate_wildcard_options( if clause.attribute == "?": options = set(list(ldf.columns)) # all attributes if clause.data_type != "": - options = options.intersection( - set(ldf.data_type[clause.data_type]) - ) + options = options.intersection(set(ldf.data_type[clause.data_type])) if clause.data_model != "": - options = options.intersection( - set(ldf.data_model[clause.data_model]) - ) + options = options.intersection(set(ldf.data_model[clause.data_model])) options = list(options) else: options = convert_to_list(clause.attribute) diff --git a/lux/processor/Parser.py b/lux/processor/Parser.py index c1852021..065d420e 100644 --- a/lux/processor/Parser.py +++ b/lux/processor/Parser.py @@ -54,9 +54,8 @@ def parse(intent: List[Union[Clause, str]]) -> List[Clause]: if isinstance(clause, list): valid_values = [] for v in clause: - if ( - type(v) is str - ): # and v in list(ldf.columns): #TODO: Move validation check to Validator + # and v in list(ldf.columns): #TODO: Move validation check to Validator + if type(v) is str: valid_values.append(v) temp_spec = Clause(attribute=valid_values) new_context.append(temp_spec) @@ -95,13 +94,10 @@ def parse(intent: List[Union[Clause, str]]) -> List[Clause]: if clause.description: # TODO: Move validation check to Validator # if ((clause.description in list(ldf.columns)) or clause.description == "?"):# if clause.description in the list of attributes - if any( - ext in [">", "<", "=", "!="] for ext in clause.description - ): # clause.description contain ">","<". or "=" + # clause.description contain ">","<". or "=" + if any(ext in [">", "<", "=", "!="] for ext in clause.description): # then parse it and assign to clause.attribute, clause.filter_op, clause.values - clause.filter_op = re.findall( - r"/.*/|>|=|<|>=|<=|!=", clause.description - )[0] + clause.filter_op = re.findall(r"/.*/|>|=|<|>=|<=|!=", clause.description)[0] split_description = clause.description.split(clause.filter_op) clause.attribute = split_description[0] clause.value = split_description[1] diff --git a/lux/processor/Validator.py b/lux/processor/Validator.py index 688a5f05..a497045a 100644 --- a/lux/processor/Validator.py +++ b/lux/processor/Validator.py @@ -54,8 +54,7 @@ def validate_intent(intent: List[Clause], ldf: LuxDataFrame) -> None: def validate_clause(clause): if not ( - (clause.attribute and clause.attribute == "?") - or (clause.value and clause.value == "?") + (clause.attribute and clause.attribute == "?") or (clause.value and clause.value == "?") ): if isinstance(clause.attribute, list): for attr in clause.attribute: @@ -66,18 +65,12 @@ def validate_clause(clause): else: if clause.attribute != "Record": # we don't value check datetime since datetime can take filter values that don't exactly match the exact TimeStamp representation - if clause.attribute and not is_datetime_string( - clause.attribute - ): + if clause.attribute and not is_datetime_string(clause.attribute): if not clause.attribute in list(ldf.columns): warnings.warn( f"The input attribute '{clause.attribute}' does not exist in the DataFrame." ) - if ( - clause.value - and clause.attribute - and clause.filter_op == "=" - ): + if clause.value and clause.attribute and clause.filter_op == "=": series = ldf[clause.attribute] if not is_datetime_series(series): if isinstance(clause.value, list): @@ -85,9 +78,8 @@ def validate_clause(clause): else: vals = [clause.value] for val in vals: - if ( - val not in series.values - ): # (not series.str.contains(val).any()): + # (not series.str.contains(val).any()): + if val not in series.values: warnings.warn( f"The input value '{val}' does not exist for the attribute '{clause.attribute}' for the DataFrame." ) diff --git a/lux/utils/date_utils.py b/lux/utils/date_utils.py index eb067ea6..d3ed03ae 100644 --- a/lux/utils/date_utils.py +++ b/lux/utils/date_utils.py @@ -40,9 +40,9 @@ def date_formatter(time_stamp, ldf): """ datetime = pd.to_datetime(time_stamp) if ldf.data_type["temporal"]: - date_column = ldf[ - ldf.data_type["temporal"][0] - ] # assumes only one temporal column, may need to change this function to recieve multiple temporal columns in the future + # assumes only one temporal column, may need to change this function to recieve multiple temporal columns in the future + date_column = ldf[ldf.data_type["temporal"][0]] + granularity = compute_date_granularity(date_column) date_str = "" if granularity == "year": @@ -50,9 +50,7 @@ def date_formatter(time_stamp, ldf): elif granularity == "month": date_str += str(datetime.year) + "-" + str(datetime.month) elif granularity == "day": - date_str += ( - str(datetime.year) + "-" + str(datetime.month) + "-" + str(datetime.day) - ) + date_str += str(datetime.year) + "-" + str(datetime.month) + "-" + str(datetime.day) else: # non supported granularity return datetime.date() @@ -80,16 +78,12 @@ def compute_date_granularity(date_column: pd.core.series.Series): field: str A str specifying the granularity of dates for the inspected temporal column """ - date_fields = [ - "day", - "month", - "year", - ] # supporting a limited set of Vega-Lite TimeUnit (https://vega.github.io/vega-lite/docs/timeunit.html) + # supporting a limited set of Vega-Lite TimeUnit (https://vega.github.io/vega-lite/docs/timeunit.html) + date_fields = ["day", "month", "year"] date_index = pd.DatetimeIndex(date_column) for field in date_fields: - if ( - hasattr(date_index, field) and len(getattr(date_index, field).unique()) != 1 - ): # can be changed to sum(getattr(date_index, field)) != 0 + # can be changed to sum(getattr(date_index, field)) != 0 + if hasattr(date_index, field) and len(getattr(date_index, field).unique()) != 1: return field return "year" # if none, then return year by default @@ -107,9 +101,7 @@ def is_datetime_series(series: pd.Series) -> bool: ------- is_date: bool """ - return pd.api.types.is_datetime64_any_dtype(series) or pd.api.types.is_period_dtype( - series - ) + return pd.api.types.is_datetime64_any_dtype(series) or pd.api.types.is_period_dtype(series) def is_datetime_string(string: str) -> bool: diff --git a/lux/utils/message.py b/lux/utils/message.py index 638fd581..04d1cc37 100644 --- a/lux/utils/message.py +++ b/lux/utils/message.py @@ -29,9 +29,7 @@ def to_html(self): if len(self.messages) == 0: return "" else: - sorted_msgs = sorted( - self.messages, key=lambda i: i["priority"], reverse=True - ) + sorted_msgs = sorted(self.messages, key=lambda i: i["priority"], reverse=True) html = "