diff --git a/.travis.yml b/.travis.yml index 82d57714..84e43fea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,18 +4,10 @@ python: services: - postgresql install: + - pip install jupyter-client==6.1.6 - 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 -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 -# - psql -c "CREATE DATABASE postgres;" -U postgres -# command to upload data to test environment SQL database and run tests +# command to run tests script: - python lux/data/upload_car_data.py - black --target-version py37 --line-length 105 --check . diff --git a/README.md b/README.md index 38098805..062a94db 100644 --- a/README.md +++ b/README.md @@ -157,8 +157,7 @@ To use Lux in [Jupyter Lab](https://github.com/jupyterlab/jupyterlab), activate jupyter labextension install @jupyter-widgets/jupyterlab-manager jupyter labextension install luxwidget ``` - -Note that JupyterLab and VSCode is supported only for lux-widget version >=0.1.2, if you have an earlier version, please upgrade to the latest version of [lux-widget](https://pypi.org/project/lux-widget/). Lux currently only works with the Chrome browser. +Lux is only compatible with Jupyter Lab version 2.2.9 and below. Support for the recent [JupyterLab 3](https://blog.jupyter.org/jupyterlab-3-0-is-out-4f58385e25bb) will come soon. Note that JupyterLab and VSCode is supported only for lux-widget version >=0.1.2, if you have an earlier version, please upgrade to the latest version of [lux-widget](https://pypi.org/project/lux-widget/). Lux currently only works with the Chrome browser. If you encounter issues with the installation, please refer to [this page](https://lux-api.readthedocs.io/en/latest/source/guide/FAQ.html#troubleshooting-tips) to troubleshoot the installation. Follow [these instructions](https://lux-api.readthedocs.io/en/latest/source/getting_started/installation.html#manual-installation-dev-setup) to set up Lux for development purposes. diff --git a/doc/source/reference/gen/lux.vis.VisList.VisList.rst b/doc/source/reference/gen/lux.vis.VisList.VisList.rst index daf9c501..f22b5bae 100644 --- a/doc/source/reference/gen/lux.vis.VisList.VisList.rst +++ b/doc/source/reference/gen/lux.vis.VisList.VisList.rst @@ -14,7 +14,6 @@ lux.vis.VisList.VisList .. autosummary:: ~VisList.__init__ - ~VisList.bottomK ~VisList.get ~VisList.map ~VisList.normalize_score @@ -23,8 +22,8 @@ lux.vis.VisList.VisList ~VisList.remove_index ~VisList.set ~VisList.set_intent + ~VisList.showK ~VisList.sort - ~VisList.topK diff --git a/doc/source/reference/gen/lux.vislib.altair.AltairChart.AltairChart.rst b/doc/source/reference/gen/lux.vislib.altair.AltairChart.AltairChart.rst index accd69eb..b2cafeed 100644 --- a/doc/source/reference/gen/lux.vislib.altair.AltairChart.AltairChart.rst +++ b/doc/source/reference/gen/lux.vislib.altair.AltairChart.AltairChart.rst @@ -19,6 +19,7 @@ lux.vislib.altair.AltairChart.AltairChart ~AltairChart.apply_default_config ~AltairChart.encode_color ~AltairChart.initialize_chart + ~AltairChart.sanitize_dataframe diff --git a/doc/source/reference/gen/lux.vislib.altair.BarChart.BarChart.rst b/doc/source/reference/gen/lux.vislib.altair.BarChart.BarChart.rst index 5c4878f8..b55c95b3 100644 --- a/doc/source/reference/gen/lux.vislib.altair.BarChart.BarChart.rst +++ b/doc/source/reference/gen/lux.vislib.altair.BarChart.BarChart.rst @@ -20,6 +20,7 @@ lux.vislib.altair.BarChart.BarChart ~BarChart.apply_default_config ~BarChart.encode_color ~BarChart.initialize_chart + ~BarChart.sanitize_dataframe diff --git a/doc/source/reference/gen/lux.vislib.altair.Histogram.Histogram.rst b/doc/source/reference/gen/lux.vislib.altair.Histogram.Histogram.rst index 47733466..920d6394 100644 --- a/doc/source/reference/gen/lux.vislib.altair.Histogram.Histogram.rst +++ b/doc/source/reference/gen/lux.vislib.altair.Histogram.Histogram.rst @@ -19,6 +19,7 @@ lux.vislib.altair.Histogram.Histogram ~Histogram.apply_default_config ~Histogram.encode_color ~Histogram.initialize_chart + ~Histogram.sanitize_dataframe diff --git a/doc/source/reference/gen/lux.vislib.altair.LineChart.LineChart.rst b/doc/source/reference/gen/lux.vislib.altair.LineChart.LineChart.rst index 3143e2f9..89257108 100644 --- a/doc/source/reference/gen/lux.vislib.altair.LineChart.LineChart.rst +++ b/doc/source/reference/gen/lux.vislib.altair.LineChart.LineChart.rst @@ -19,6 +19,7 @@ lux.vislib.altair.LineChart.LineChart ~LineChart.apply_default_config ~LineChart.encode_color ~LineChart.initialize_chart + ~LineChart.sanitize_dataframe diff --git a/doc/source/reference/gen/lux.vislib.altair.ScatterChart.ScatterChart.rst b/doc/source/reference/gen/lux.vislib.altair.ScatterChart.ScatterChart.rst index f7a1d283..be0569f7 100644 --- a/doc/source/reference/gen/lux.vislib.altair.ScatterChart.ScatterChart.rst +++ b/doc/source/reference/gen/lux.vislib.altair.ScatterChart.ScatterChart.rst @@ -19,6 +19,7 @@ lux.vislib.altair.ScatterChart.ScatterChart ~ScatterChart.apply_default_config ~ScatterChart.encode_color ~ScatterChart.initialize_chart + ~ScatterChart.sanitize_dataframe diff --git a/lux/_config/config.py b/lux/_config/config.py index 91b2b2ef..cb4b0a7b 100644 --- a/lux/_config/config.py +++ b/lux/_config/config.py @@ -3,7 +3,8 @@ 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 +from typing import Any, Callable, Dict, Iterable, List, Optional, Union +import lux import warnings import lux diff --git a/lux/_version.py b/lux/_version.py index bd12c3d5..718a587d 100644 --- a/lux/_version.py +++ b/lux/_version.py @@ -1,5 +1,5 @@ #!/usr/bin/env python # coding: utf-8 -version_info = (0, 2, 1, 2) +version_info = (0, 2, 2) __version__ = ".".join(map(str, version_info)) diff --git a/lux/action/correlation.py b/lux/action/correlation.py index 4ba9b5e9..98920384 100644 --- a/lux/action/correlation.py +++ b/lux/action/correlation.py @@ -76,7 +76,8 @@ def correlation(ldf: LuxDataFrame, ignore_transpose: bool = True): if ignore_rec_flag: recommendation["collection"] = [] return recommendation - vlist = vlist.topK(15) + vlist.sort() + vlist = vlist.showK() recommendation["collection"] = vlist return recommendation diff --git a/lux/action/enhance.py b/lux/action/enhance.py index 94a4ea60..c6f240eb 100644 --- a/lux/action/enhance.py +++ b/lux/action/enhance.py @@ -66,6 +66,7 @@ def enhance(ldf): for vis in vlist: vis.score = interestingness(vis, ldf) - vlist = vlist.topK(15) + vlist.sort() + vlist = vlist.showK() recommendation["collection"] = vlist return recommendation diff --git a/lux/action/generalize.py b/lux/action/generalize.py index 91b83239..45e9d0f8 100644 --- a/lux/action/generalize.py +++ b/lux/action/generalize.py @@ -93,5 +93,6 @@ def generalize(ldf): vlist.remove_duplicates() vlist.sort(remove_invalid=True) + vlist._collection = list(filter(lambda x: x.score != -1, vlist._collection)) recommendation["collection"] = vlist return recommendation diff --git a/lux/action/univariate.py b/lux/action/univariate.py index ef37efef..1ffacdea 100644 --- a/lux/action/univariate.py +++ b/lux/action/univariate.py @@ -82,7 +82,6 @@ def univariate(ldf, *args): 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 9a13cd20..b1a69371 100644 --- a/lux/core/__init__.py +++ b/lux/core/__init__.py @@ -26,8 +26,38 @@ def setOption(overridePandas=True): if overridePandas: pd.DataFrame = ( pd.io.json._json.DataFrame - ) = pd.io.parsers.DataFrame = pd.core.frame.DataFrame = LuxDataFrame - pd.Series = LuxSeries + ) = ( + pd.io.parsers.DataFrame + ) = ( + pd.io.sql.DataFrame + ) = ( + pd.io.excel.DataFrame + ) = ( + pd.io.formats.DataFrame + ) = ( + pd.io.sas.DataFrame + ) = ( + pd.io.clipboards.DataFrame + ) = ( + pd.io.common.DataFrame + ) = ( + pd.io.feather_format.DataFrame + ) = ( + pd.io.gbq.DataFrame + ) = ( + pd.io.html.DataFrame + ) = ( + pd.io.orc.DataFrame + ) = ( + pd.io.parquet.DataFrame + ) = ( + pd.io.pickle.DataFrame + ) = ( + pd.io.pytables.DataFrame + ) = ( + pd.io.spss.DataFrame + ) = pd.io.stata.DataFrame = pd.io.api.DataFrame = pd.core.frame.DataFrame = LuxDataFrame + pd.Series = pd.core.series.Series = LuxSeries else: pd.DataFrame = pd.io.parsers.DataFrame = pd.core.frame.DataFrame = originalDF pd.Series = originalSeries diff --git a/lux/executor/PandasExecutor.py b/lux/executor/PandasExecutor.py index 888b02f5..691594d1 100644 --- a/lux/executor/PandasExecutor.py +++ b/lux/executor/PandasExecutor.py @@ -237,10 +237,20 @@ 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.dropna(subset=[measure_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") + + vis._vis_data = vis._vis_data.dropna(subset=[measure_attr.attribute]) + try: + vis._vis_data = vis._vis_data.sort_values(by=groupby_attr.attribute, ascending=True) + except TypeError: + warnings.warn( + f"\nLux detects that the attribute '{groupby_attr.attribute}' maybe contain mixed type." + + f"\nTo visualize this attribute, you may want to convert the '{groupby_attr.attribute}' into a uniform type as follows:" + + f"\n\tdf['{groupby_attr.attribute}'] = df['{groupby_attr.attribute}'].astype(str)" + ) + vis._vis_data[groupby_attr.attribute] = vis._vis_data[groupby_attr.attribute].astype(str) + vis._vis_data = vis._vis_data.sort_values(by=groupby_attr.attribute, ascending=True) + vis._vis_data = vis._vis_data.reset_index() + vis._vis_data = vis._vis_data.drop(columns="index") @staticmethod def execute_binning(vis: Vis): diff --git a/lux/vis/VisList.py b/lux/vis/VisList.py index a346e6cc..e3bdfa3e 100644 --- a/lux/vis/VisList.py +++ b/lux/vis/VisList.py @@ -233,18 +233,22 @@ 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)) + if lux.config.sort == "none": + return + elif lux.config.sort == "ascending": + descending = False + elif lux.config.sort == "descending": + descending = True # 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 showK(self): + k = lux.config.topk + if k == False: + return self + elif isinstance(k, int): + k = abs(k) + return VisList(self._collection[:k]) def normalize_score(self, invert_order=False): max_score = max(list(self.get("score"))) diff --git a/requirements-dev.txt b/requirements-dev.txt index 0365d5bd..4e0e654d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,4 +2,5 @@ pytest>=5.3.1 pytest-cov>=2.8.1 Sphinx>=3.0.2 sphinx-rtd-theme>=0.4.3 +xlrd black diff --git a/tests/test_config.py b/tests/test_config.py index 8441498c..11bcb732 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -29,7 +29,8 @@ def random_categorical(ldf): vlist = VisList(intent, ldf) for vis in vlist: vis.score = 10 - vlist = vlist.topK(15) + vlist.sort() + vlist = vlist.showK() return { "action": "bars", "description": "Random list of Bar charts", @@ -106,7 +107,8 @@ def random_categorical(ldf): vlist = VisList(intent, ldf) for vis in vlist: vis.score = 10 - vlist = vlist.topK(15) + vlist.sort() + vlist = vlist.showK() return { "action": "bars", "description": "Random list of Bar charts", diff --git a/tests/test_pandas_coverage.py b/tests/test_pandas_coverage.py index e861769b..83a26d92 100644 --- a/tests/test_pandas_coverage.py +++ b/tests/test_pandas_coverage.py @@ -15,6 +15,8 @@ from .context import lux import pytest import pandas as pd +import numpy as np +import warnings ################### # DataFrame Tests # @@ -605,7 +607,7 @@ def test_value_counts(global_var): 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 type(df["Brand"].value_counts()) == lux.core.series.LuxSeries assert df["Weight"]._metadata == [ "_intent", "data_type", @@ -677,4 +679,4 @@ def test_read_sas(global_var): df = pd.read_sas(url, format="sas7bdat") df._repr_html_() assert list(df.recommendation.keys()) == ["Correlation", "Distribution", "Temporal"] - assert len(df.data_type) == 6 + assert len(df.data_type) == 6 \ No newline at end of file diff --git a/tests/test_series.py b/tests/test_series.py index 62a4697f..75a93691 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -50,4 +50,4 @@ def test_print_dtypes(global_var): df = pytest.college_df with warnings.catch_warnings(record=True) as w: print(df.dtypes) - assert len(w) == 0, "Warning displayed when printing dtypes" + assert len(w) == 0, "Warning displayed when printing dtypes" \ No newline at end of file