Repository: gradio-app/gradio

Issue:
Dynamic Dataset Size for Dataframe Component
is there any way we can dynamically handle consuming of the dataframe in the background without limiting users to upload a certain size? 

Like when we create the dataframe component we have to give a list to headers which should match the number of columns in a dataset in a CSV/TSV file you're dragging to UI. This is not good for below reasons:
- one thing in tabular workflows is that we have so many feature engineering going on, we add modify datasets by adding one hot encoded features or eliminate features according to shap or importance, it would be reaaaalllly essential to have such feature.
- not so many people know about the number of columns of their dataset in a CSV file beforehand.

maybe ping @pngwn  for this 🙂
thought: We can change the size through UI, so it should be fine for CSV/TSV file read to have.


Discussion:
Yesssss let's do this
I think this is a duplicate of #1198 

Resolving patch:
diff --git a/gradio/components.py b/gradio/components.py
--- a/gradio/components.py
+++ b/gradio/components.py
@@ -16,7 +16,7 @@
 import warnings
 from copy import deepcopy
 from types import ModuleType
-from typing import Any, Callable, Dict, List, Optional, Tuple
+from typing import Any, Callable, Dict, List, Optional, Tuple, TypedDict
 
 import matplotlib.figure
 import numpy as np
@@ -2389,7 +2389,11 @@ def style(
         )
 
 
-@document()
+class DataframeData(TypedDict):
+    headers: List[str]
+    data: List[List[str | int | bool]]
+
+
 class Dataframe(Changeable, IOComponent):
     """
     Accepts or displays 2D input through a spreadsheet-like component for dataframes.
@@ -2521,26 +2525,22 @@ def update(
         }
         return IOComponent.add_interactive_to_config(updated_config, interactive)
 
-    def preprocess(
-        self, x: List[List[str | Number | bool]]
-    ) -> pd.DataFrame | np.ndarray | List[List[str | float | bool]]:
+    def preprocess(self, x: DataframeData):
         """
         Parameters:
-            x: 2D array of str, numeric, or bool data
+        x (Dict[headers: List[str], data: List[List[str | int | bool]]]): 2D array of str, numeric, or bool data
         Returns:
             Dataframe in requested format
         """
         if self.type == "pandas":
-            if self.headers:
-                return pd.DataFrame(x, columns=self.headers)
+            if x.get("headers") is not None:
+                return pd.DataFrame(x["data"], columns=x.get("headers"))
             else:
-                return pd.DataFrame(x)
-        if self.col_count[0] == 1:
-            x = [row[0] for row in x]
+                return pd.DataFrame(x["data"])
         if self.type == "numpy":
-            return np.array(x)
+            return np.array(x["data"])
         elif self.type == "array":
-            return x
+            return x["data"]
         else:
             raise ValueError(
                 "Unknown type: "
@@ -2587,8 +2587,6 @@ def postprocess(self, y: str | pd.DataFrame | np.ndarray | List[List[str | float
         if isinstance(y, (np.ndarray, list)):
             if isinstance(y, np.ndarray):
                 y = y.tolist()
-            if len(y) == 0 or not isinstance(y[0], list):
-                y = [y]
             return {
                 "data": Dataframe.__process_markdown(y, self.datatype),
             }


Test patch:
diff --git a/test/test_components.py b/test/test_components.py
--- a/test/test_components.py
+++ b/test/test_components.py
@@ -999,7 +999,10 @@ def test_component_functions(self):
         """
         Preprocess, serialize, save_flagged, restore_flagged, generate_sample, get_config
         """
-        x_data = [["Tim", 12, False], ["Jan", 24, True]]
+        x_data = {
+            "data": [["Tim", 12, False], ["Jan", 24, True]],
+            "headers": ["Name", "Age", "Member"],
+        }
         dataframe_input = gr.Dataframe(headers=["Name", "Age", "Member"])
         output = dataframe_input.preprocess(x_data)
         self.assertEqual(output["Age"][1], 24)
@@ -1046,7 +1049,7 @@ def test_component_functions(self):
         )
         dataframe_input = gr.Dataframe()
         output = dataframe_input.preprocess(x_data)
-        self.assertEqual(output[1][1], 24)
+        self.assertEqual(output["Age"][1], 24)
         with self.assertRaises(ValueError):
             wrong_type = gr.Dataframe(type="unknown")
             wrong_type.preprocess(x_data)
@@ -1120,13 +1123,13 @@ def test_in_interface_as_input(self):
         """
         Interface, process,
         """
-        x_data = [[1, 2, 3], [4, 5, 6]]
+        x_data = {"data": [[1, 2, 3], [4, 5, 6]]}
         iface = gr.Interface(np.max, "numpy", "number")
         self.assertEqual(iface.process([x_data]), [6])
-        x_data = [["Tim"], ["Jon"], ["Sal"]]
+        x_data = {"data": [["Tim"], ["Jon"], ["Sal"]]}
 
         def get_last(my_list):
-            return my_list[-1]
+            return my_list[-1][-1]
 
         iface = gr.Interface(get_last, "list", "text")
         self.assertEqual(iface.process([x_data]), ["Sal"])
@@ -1140,7 +1143,9 @@ def check_odd(array):
             return array % 2 == 0
 
         iface = gr.Interface(check_odd, "numpy", "numpy")
-        self.assertEqual(iface.process([[2, 3, 4]])[0], {"data": [[True, False, True]]})
+        self.assertEqual(
+            iface.process([{"data": [[2, 3, 4]]}])[0], {"data": [[True, False, True]]}
+        )
 
 
 class TestVideo(unittest.TestCase):
@@ -1356,14 +1361,16 @@ def test_in_interface_as_output(self):
         """
         timeseries_output = gr.Timeseries(x="time", y=["retail", "food", "other"])
         iface = gr.Interface(lambda x: x, "dataframe", timeseries_output)
-        df = pd.DataFrame(
-            {
-                "time": [1, 2, 3, 4],
-                "retail": [1, 2, 3, 2],
-                "food": [1, 2, 3, 2],
-                "other": [1, 2, 4, 2],
-            }
-        )
+        df = {
+            "data": pd.DataFrame(
+                {
+                    "time": [1, 2, 3, 4],
+                    "retail": [1, 2, 3, 2],
+                    "food": [1, 2, 3, 2],
+                    "other": [1, 2, 4, 2],
+                }
+            )
+        }
         self.assertEqual(
             iface.process([df]),
             [
@@ -1585,6 +1592,7 @@ def test_in_interface(self):
         """
 
         def get_avg_age_per_gender(data):
+            print(data)
             return {
                 "M": int(data[data["gender"] == "M"].mean()),
                 "F": int(data[data["gender"] == "F"].mean()),
@@ -1603,7 +1611,10 @@ def get_avg_age_per_gender(data):
             ["O", 20],
             ["F", 30],
         ]
-        self.assertDictEqual(iface.process([y_data])[0], {"M": 35, "F": 25, "O": 20})
+        self.assertDictEqual(
+            iface.process([{"data": y_data, "headers": ["gender", "age"]}])[0],
+            {"M": 35, "F": 25, "O": 20},
+        )
 
 
 class TestHTML(unittest.TestCase):