Repository: lux-org/lux (language: python)

Issue:
New action: "All-column" vis when only few columns in dataframe
When there are only two columns in a dataframe involving a quantitative and a temporal attribute, we display separate univariate charts. The better chart to display here would be a line chart with both attributes. We need to change the action logic to account for this use case.

```python
tmax = pd.read_csv('https://raw.githubusercontent.com/koldunovn/python_for_geosciences/master/DelhiTmax.txt', delimiter=r"\s+", parse_dates=[[0,1,2]], header=None)
tmax.columns = ['Date', 'Temp']
```

![image](https://user-images.githubusercontent.com/5554675/103758845-bd166f00-504d-11eb-8cda-09d4c46233bd.png)

[This notebook](https://github.com/koldunovn/python_for_geosciences/blob/master/06%20-%20Time%20series%20(Pandas).ipynb) has a good use case example for time series.




Discussion / hints:
A similar use case is when there are 1 dimension and 2 quantitative variables, the visualization shows as two separate charts.
![image](https://user-images.githubusercontent.com/5554675/104676717-e35d9e00-5722-11eb-8581-ab448dd2d64e.png)
![image](https://user-images.githubusercontent.com/5554675/104676725-e8225200-5722-11eb-9bf7-8d650801e749.png)
A single visualization like this would be more appropriate

![image](https://user-images.githubusercontent.com/5554675/104676820-1ef86800-5723-11eb-90e5-79a45b1bd72d.png)
![image](https://user-images.githubusercontent.com/5554675/104676837-26b80c80-5723-11eb-948d-32e9eac1d998.png)

Example code:
```python
df = pd.DataFrame({'nPts': {0: 49999, 1: 71174, 2: 101317, 3: 144224, 4: 205303, 5: 292249, 6: 416016, 7: 592198, 8: 842993, 9: 1200000}, 't_heatmap': {0: 0.15121674537658691, 1: 0.1811518669128418, 2: 0.2179429531097412, 3: 0.2787730693817139, 4: 0.3973350524902344, 5: 0.4233138561248779, 6: 0.580251932144165, 7: 0.7928099632263184, 8: 1.0876789093017578, 9: 1.6242818832397459}, 't_color_heatmap': {0: 0.14242982864379886, 1: 0.18866705894470212, 2: 0.1566781997680664, 3: 0.16737699508666992, 4: 0.19900894165039065, 5: 0.2701129913330078, 6: 0.2533812522888184, 7: 0.37183785438537603, 8: 0.3830866813659668, 9: 0.39321017265319824}, 't_bar': {0: 0.01940608024597168, 1: 0.02618718147277832, 2: 0.024693727493286133, 3: 0.029685020446777344, 4: 0.03471803665161133, 5: 0.04173588752746582, 6: 0.04706382751464844, 7: 0.0667569637298584, 8: 0.08260798454284668, 9: 0.1006929874420166}, 't_cbar': {0: 0.04185795783996582, 1: 0.050965070724487305, 2: 0.052091121673583984, 3: 0.0610501766204834, 4: 0.07504606246948242, 5: 0.09924101829528807, 6: 0.10392117500305176, 7: 0.13044309616088867, 8: 0.18137121200561526, 9: 0.2069528102874756}, 't_hist': {0: 0.01846003532409668, 1: 0.018136978149414062, 2: 0.018748998641967773, 3: 0.01876473426818848, 4: 0.02434706687927246, 5: 0.025368213653564453, 6: 0.02530217170715332, 7: 0.02823114395141602, 8: 0.029034852981567383, 9: 0.034781217575073235}, 't_scatter': {0: 0.8179378509521484, 1: 1.0982017517089844, 2: 1.4875690937042236, 3: 2.146117925643921, 4: 3.1123709678649902, 5: 3.92416787147522, 6: 6.097048044204713, 7: 8.803220987319945, 8: 12.054464101791382, 9: 17.740845680236816}, 't_color_scatter': {0: 1.3474130630493164, 1: 1.7953579425811768, 2: 2.6268541812896733, 3: 3.646371126174927, 4: 4.901940107345581, 5: 6.974237203598023, 6: 10.197483777999876, 7: 14.134275913238525, 8: 21.13951110839844, 9: 27.127063989639282}})
pdf = df.melt(id_vars=["nPts"],value_vars=['t_heatmap', 't_color_heatmap', 't_bar', 't_cbar', 't_hist','t_scatter', 't_color_scatter'], var_name="type", value_name='time')
```

Fix patch:
diff --git a/lux/core/frame.py b/lux/core/frame.py
index 8d291b9c..3ad6882c 100644
--- a/lux/core/frame.py
+++ b/lux/core/frame.py
@@ -90,6 +90,7 @@ def __init__(self, *args, **kw):
         self.pre_aggregated = None
         self._type_override = {}
         warnings.formatwarning = lux.warning_format
+        self.all_column = False
 
     @property
     def _constructor(self):
@@ -344,6 +345,21 @@ def _append_rec(self, rec_infolist, recommendations: Dict):
         if recommendations["collection"] is not None and len(recommendations["collection"]) > 0:
             rec_infolist.append(recommendations)
 
+    def show_all_column_vis(self):
+        from lux.processor.Compiler import Compiler
+
+        quantitative_columns = [i for i in self.data_type if i != "quantitative"]
+        curr_vis = VisList([i for i in self.columns], self)
+        if len(curr_vis) > 0:
+            Compiler.determine_encoding(self, curr_vis[0])
+            if (
+                curr_vis[0]._inferred_intent != []
+                and self._intent == []
+                and (len(quantitative_columns) == 2 or len(quantitative_columns) == 3)
+            ):
+                self.current_vis = curr_vis
+                self.all_column = True
+
     def maintain_recs(self, is_series="DataFrame"):
         # `rec_df` is the dataframe to generate the recommendations on
         # check to see if globally defined actions have been registered/removed
@@ -418,9 +434,11 @@ def maintain_recs(self, is_series="DataFrame"):
                 if len(vlist) > 0:
                     rec_df._recommendation[action_type] = vlist
             rec_df._rec_info = rec_infolist
+            rec_df.show_all_column_vis()
             self._widget = rec_df.render_widget()
         # re-render widget for the current dataframe if previous rec is not recomputed
         elif show_prev:
+            rec_df.show_all_column_vis()
             self._widget = rec_df.render_widget()
         self._recs_fresh = True
 
@@ -652,6 +670,7 @@ def render_widget(self, renderer: str = "altair", input_current_vis=""):
             recommendations=widgetJSON["recommendation"],
             intent=LuxDataFrame.intent_to_string(self._intent),
             message=self._message.to_html(),
+            allColumn=self.all_column,
         )
 
     @staticmethod


Test patch:
diff --git a/tests/test_vis.py b/tests/test_vis.py
index 15c75017..24d9b73a 100644
--- a/tests/test_vis.py
+++ b/tests/test_vis.py
@@ -547,3 +547,37 @@ def test_matplotlib_heatmap_flag_config():
     assert not df.recommendation["Correlation"][0]._postbin
     lux.config.heatmap = True
     lux.config.plotting_backend = "vegalite"
+
+
+def test_all_column_current_vis():
+    df = pd.read_csv(
+        "https://raw.githubusercontent.com/koldunovn/python_for_geosciences/master/DelhiTmax.txt",
+        delimiter=r"\s+",
+        parse_dates=[[0, 1, 2]],
+        header=None,
+    )
+    df.columns = ["Date", "Temp"]
+    df._ipython_display_()
+    assert df.current_vis != None
+
+
+def test_all_column_current_vis_filter():
+    lux.config.plotting_backend = "matplotlib"
+    df = pytest.car_df
+    two_col_df = df[["Year", "Displacement"]]
+    two_col_df._ipython_display_()
+    assert two_col_df.current_vis != None
+
+    three_col_df = df[["Year", "Displacement", "Origin"]]
+    three_col_df._ipython_display_()
+
+
+def test_intent_override_all_column():
+    lux.config.plotting_backend = "matplotlib"
+    df = pytest.car_df
+    df = df[["Year", "Displacement"]]
+    df.intent = ["Year"]
+    df._ipython_display_()
+    current_vis_code = df.current_vis[0].to_matplotlib()
+    assert "ax.set_ylabel('Number of Records')" in current_vis_code, "All column not overriden by intent"
+    lux.config.plotting_backend = "altair"