Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add interactive DataFrame pane (#560)
- Loading branch information
1 parent
72d9bb1
commit 45177aa
Showing
8 changed files
with
430 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import pandas as pd\n", | ||
"import panel as pn\n", | ||
"\n", | ||
"pn.extension()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The ``DataFrame`` widget allows displaying and editing a pandas DataFrame.\n", | ||
"\n", | ||
"For more information about listening to widget events and laying out widgets refer to the [widgets user guide](../../user_guide/Widgets.ipynb). Alternatively you can learn how to build GUIs by declaring parameters independently of any specific widgets in the [param user guide](../../user_guide/Param.ipynb). To express interactivity entirely using Javascript without the need for a Python server take a look at the [links user guide](../../user_guide/Param.ipynb).\n", | ||
"\n", | ||
"#### Parameters:\n", | ||
"\n", | ||
"For layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n", | ||
"\n", | ||
"##### Core\n", | ||
"\n", | ||
"* **``editors``** (``dict``): A dictionary mapping from column name to a bokeh CellEditor instance, which overrides the default.\n", | ||
"* **``fit_column``** (``boolean``): Whether columns should be fit to the available width. \n", | ||
"* **``formatters``** (``dict``): A dictionary mapping from column name to a bokeh CellFormatter instance, which overrides the default.\n", | ||
"* **``row_height``** (``int``): The height of each table row.\n", | ||
"* **``selection``** (``list``) The currently selected rows \n", | ||
"* **``value``** (``pd.DataFrame``): The pandas DataFrame to display and edit\n", | ||
"* **``widths``** (``dict``): A dictionary mapping from column name to column width in the rendered table.\n", | ||
"\n", | ||
"##### Display\n", | ||
"\n", | ||
"* **``disabled``** (boolean): Whether the widget is editable\n", | ||
"* **``name``** (str): The title of the widget\n", | ||
"\n", | ||
"___" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The ``DataFrame`` widget renders an table which allows directly editing the contents of the dataframe with any changes being synced with Python. Note that it modifies the ``pd.DataFrame`` in place." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"df = pd.DataFrame({'int': [1, 2, 3], 'float': [3.14, 6.28, 9.42], 'str': ['A', 'B', 'C']}, index=[1, 2, 3])\n", | ||
"\n", | ||
"pn.widgets.DataFrame(df)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"By default the widget will pick bokeh ``CellFormatter`` and ``CellEditor`` types appropriate to the dtype of the column. These may be overriden by explicit dictionaries mapping from the column name to the editor or formatter instance. For example below we create a ``SelectEditor`` instance to pick from four options in the ``str`` column and a ``NumberFormatter`` to customize the formatting of the float values:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"from bokeh.models.widgets.tables import SelectEditor, NumberFormatter\n", | ||
"\n", | ||
"editor = SelectEditor(options=['A', 'B', 'C', 'D'])\n", | ||
"formatter = NumberFormatter(format='0.00000') \n", | ||
"\n", | ||
"table = pn.widgets.DataFrame(df, editors={'str': editor}, formatters={'float': formatter})\n", | ||
"table" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Once initialized the ``selection`` property will return the integer indexes of the selected rows and the ``selected_dataframe`` property will return a new DataFrame containing just the selected rows:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"table.selection = [0, 2]\n", | ||
"\n", | ||
"table.selected_dataframe" | ||
] | ||
} | ||
], | ||
"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.6.8" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 4 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
from __future__ import absolute_import, division, unicode_literals | ||
|
||
try: | ||
import pandas as pd | ||
except ImportError: | ||
pass | ||
|
||
from bokeh.models.widgets.tables import ( | ||
NumberFormatter, IntEditor, NumberEditor, StringFormatter, | ||
SelectEditor | ||
) | ||
|
||
from panel.widgets import DataFrame | ||
|
||
|
||
def test_dataframe_widget(dataframe, document, comm): | ||
|
||
table = DataFrame(dataframe) | ||
|
||
model = table.get_root(document, comm) | ||
|
||
index_col, int_col, float_col, str_col = model.columns | ||
|
||
assert index_col.title == 'index' | ||
assert isinstance(index_col.formatter, NumberFormatter) | ||
assert isinstance(index_col.editor, IntEditor) | ||
|
||
assert int_col.title == 'int' | ||
assert isinstance(int_col.formatter, NumberFormatter) | ||
assert isinstance(int_col.editor, IntEditor) | ||
|
||
assert float_col.title == 'float' | ||
assert isinstance(float_col.formatter, NumberFormatter) | ||
assert isinstance(float_col.editor, NumberEditor) | ||
|
||
assert str_col.title == 'str' | ||
assert isinstance(float_col.formatter, StringFormatter) | ||
assert isinstance(float_col.editor, NumberEditor) | ||
|
||
|
||
def test_dataframe_editors(dataframe, document, comm): | ||
editor = SelectEditor(options=['A', 'B', 'C']) | ||
table = DataFrame(dataframe, editors={'str': editor}) | ||
model = table.get_root(document, comm) | ||
|
||
assert model.columns[-1].editor is editor | ||
|
||
|
||
def test_dataframe_formatter(dataframe, document, comm): | ||
formatter = NumberFormatter(format='0.0000') | ||
table = DataFrame(dataframe, formatters={'float': formatter}) | ||
model = table.get_root(document, comm) | ||
assert model.columns[2].formatter is formatter | ||
|
||
|
||
def test_dataframe_triggers(dataframe): | ||
events = [] | ||
|
||
def increment(event, events=events): | ||
events.append(event) | ||
|
||
table = DataFrame(dataframe) | ||
table.param.watch(increment, 'value') | ||
table._process_events({'data': {'str': ['C', 'B', 'A']}}) | ||
assert len(events) == 1 | ||
|
||
|
||
def test_dataframe_does_not_trigger(dataframe): | ||
events = [] | ||
|
||
def increment(event, events=events): | ||
events.append(event) | ||
|
||
table = DataFrame(dataframe) | ||
table.param.watch(increment, 'value') | ||
table._process_events({'data': {'str': ['A', 'B', 'C']}}) | ||
assert len(events) == 0 | ||
|
||
|
||
def test_dataframe_selected_dataframe(dataframe): | ||
table = DataFrame(dataframe, selection=[0, 2]) | ||
pd.testing.assert_frame_equal(dataframe.iloc[[0, 2]], table.selected_dataframe) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.