{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Linking qgrid to other visualizations using event callbacks\n", "===============================================\n", "\n", "The following examples use qgrid's new \"events API\" that is only available in qgrid version 1.0.3 or newer. Specifically this notebook makes use of the ``on`` methods that qgrid provides for attaching event handlers. Event handlers can be attached using the ``qgrid.on`` method to listen for events on all widgets, or using the ``QgridWidget.on`` method the to listen for events on individual QgridWidget instances. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 1 - Filter matplotlib scatter plot using qgrid\n", "In this example you'll see that by listening for a QgridWidget instances's ``filter_changed`` event, we can use qgrid to filter the data that's being visualized by another control in the notebook, in this case a matplotlib scatter plot. \n", "\n", "This capability allows you to filter a visualization by ANY field in the underlying DataFrame, including fields that are not used to generate the visualization. This enables interesting workflows like using a Categorical column to mark a particular row of the DataFrame with as \"bad\", setting a filter to hide \"bad\" rows in the qgrid instance, and then seeing the \"bad\" rows also disappear from any visualizations that the qgrid instance is hooked up to.\n", "\n", "The try out a simple example of using qgrid to filter another visualization, first execute the following two cells. Once you do that you should see a qgrid instance and a matplotlib scatter plot showing the same data as the qgrid instance. Next, set some filters on the columns of the qgrid instance and watch what happens to the scatter plot. You should see it update immediately to reflect the filtering changes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import qgrid\n", "randn = np.random.randn\n", "df_types = pd.DataFrame({\n", " 'A' : 1.,\n", " 'B' : pd.Series(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',\n", " '2013-01-05', '2013-01-06', '2013-01-07', '2013-01-08', '2013-01-09'],index=list(range(9)),dtype='datetime64[ns]'),\n", " 'C' : pd.Series(randn(9),index=list(range(9)),dtype='float32'),\n", " 'D' : np.array([3] * 9,dtype='int32'),\n", " 'E' : pd.Categorical([\"washington\", \"adams\", \"washington\", \"madison\", \"lincoln\",\"jefferson\", \"hamilton\", \"roosevelt\", \"kennedy\"]),\n", " 'F' : [\"foo\", \"bar\", \"buzz\", \"bippity\",\"boppity\", \"foo\", \"foo\", \"bar\", \"zoo\"] })\n", "df_types['G'] = df_types['F'] == 'foo'\n", "qgrid_widget = qgrid.show_grid(df_types, show_toolbar=True)\n", "qgrid_widget" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib nbagg\n", "import matplotlib.pyplot as plt\n", "\n", "n = 50\n", "\n", "qgrid_df = qgrid_widget.get_changed_df()\n", "x = qgrid_df.index\n", "y = qgrid_df['C']\n", "\n", "fig, ax = plt.subplots()\n", "fit = np.polyfit(x, y, deg=1)\n", "line, = ax.plot(x, fit[0] * x + fit[1], color='red')\n", "scatter, = ax.plot(x,y,ms=8,color='b',marker='o',ls='')\n", "\n", "def handle_filter_changed(event, widget):\n", " qgrid_df = qgrid_widget.get_changed_df()\n", " x = qgrid_df.index\n", " y = qgrid_df['C']\n", " fit = np.polyfit(x, y, deg=1)\n", " line.set_data(x, fit[0] * x + fit[1])\n", " fig.canvas.draw()\n", " scatter.set_data(x, y)\n", " fig.canvas.draw()\n", "\n", "qgrid_widget.on('filter_changed', handle_filter_changed)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qgrid_widget # render the qgrid widget again so we don't have to scroll to see the changes in the scatter plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 2 - Filter matplotlib price chart using qgrid\n", "This example is basically a repeat of Example 1, except with a line chart of some pricing data for the S&P 500 instead of a scatter plot.\n", "\n", "First, execute the following two cells. Once you do that you should see a qgrid instance and a matplotlib line chart showing the same data as the qgrid instance. Next, set some filters on the columns of the qgrid instance and watch what happens to the line chart (it should update immediately to reflect the filtering changes)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import qgrid\n", "randn = np.random.randn\n", "\n", "# Get a pandas DataFrame containing the daily prices for the S&P 500 from 1/1/2014 - 1/1/2017\n", "from pandas_datareader.data import DataReader\n", "spy = DataReader(\n", " 'SPY',\n", " 'yahoo',\n", " pd.Timestamp('2014-01-01'), \n", " pd.Timestamp('2017-01-01'),\n", ")\n", "# Tell qgrid to automatically render all DataFrames and Series as qgrids.\n", "qgrid.enable()\n", "\n", "# Render the DataFrame as a qgrid automatically\n", "spy_qgrid = qgrid.show_grid(spy)\n", "spy_qgrid" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib nbagg\n", "import matplotlib.pyplot as plt\n", "import datetime\n", "import matplotlib.pyplot as plt\n", "import matplotlib.dates as mdates\n", "import matplotlib.cbook as cbook\n", "from traitlets import All\n", "\n", "years = mdates.YearLocator() # every year\n", "months = mdates.MonthLocator() # every month\n", "yearsFmt = mdates.DateFormatter('%Y')\n", "\n", "n = 50\n", "qgrid_spy_df = spy_qgrid.get_changed_df()\n", "x = qgrid_spy_df.index\n", "y = qgrid_spy_df['Close']\n", "\n", "fig, ax = plt.subplots()\n", "line, = ax.plot(x, y)\n", "\n", "# format the ticks\n", "ax.xaxis.set_major_locator(years)\n", "ax.xaxis.set_major_formatter(yearsFmt)\n", "ax.xaxis.set_minor_locator(months)\n", "\n", "datemin = datetime.date(x.min().year, 1, 1)\n", "datemax = datetime.date(x.max().year + 1, 1, 1)\n", "ax.set_xlim(datemin, datemax)\n", "\n", "# format the coords message box\n", "def price(x):\n", " return '$%1.2f' % x\n", "ax.format_xdata = mdates.DateFormatter('%Y-%m-%d')\n", "ax.format_ydata = price\n", "ax.grid(True)\n", "\n", "# rotates and right aligns the x labels, and moves the bottom of the\n", "# axes up to make room for them\n", "fig.autofmt_xdate()\n", "\n", "def handle_filter_changed(event, widget):\n", " qgrid_spy_df = widget.get_changed_df()\n", " x = qgrid_spy_df.index\n", " y = qgrid_spy_df['Close']\n", " \n", " datemin = datetime.date(x.min().year, 1, 1)\n", " datemax = datetime.date(x.max().year + 1, 1, 1)\n", " ax.set_xlim(datemin, datemax)\n", " \n", " line.set_data(x, y)\n", " fig.canvas.draw()\n", "\n", "spy_qgrid.on('filter_changed', handle_filter_changed)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "spy_qgrid # render the qgrid widget again so we don't have to scroll to see the changes in the scatter plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 3 - Logging all events\n", "In this example we'll see how you can listen for events from any qgrid instance using the `qgrid.on` method.\n", "\n", "Execute the following two cells. The first cell will create an empty output widget, and the second cell use the `on` method to listen for all events from all qgrid instances. Once the cells are executed, try interacting with any of the qgrid instances you created earlier in the notebook (i.e. by sorting, filtering, scrolling, etc). Then scroll back down to this output widget and you'll notice that any actions that you took got printed to the output widget.\n", "\n", "In practice you'd probably want to do something more interesting than just print these events to the notebook, such as log the events to a service that you use to track user interactions. This is just a proof-of-concept to show which events are available." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import ipywidgets as widgets\n", "json_updates = widgets.Output(layout={'border': '1px solid black'})\n", "user_interactions = widgets.Output(layout={'border': '1px solid black'})\n", "\n", "widgets.VBox([user_interactions, json_updates])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import qgrid\n", "from traitlets import All\n", "\n", "def handle_all_events(event, qgrid_widget):\n", " output_area = json_updates if event['name'] == 'json_updated' else user_interactions\n", " with output_area:\n", " print(event)\n", "\n", "qgrid.on(All, handle_all_events)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 4 - Filter qgrid using ipywidgets slider (experimental)\n", "In the Example 1 and 2, qgrid was being used to control other visualizations. In this example, we're going to invert the direction of control and use another visualization (in this case an ipywidget slider) to control qgrid.\n", "\n", "Please note that the `_handle_qgrid_msg_helper` method used in this example is not a documented part of the API yet and should be considered experimental. It will likely be renamed and changed slightly once it gets officially added to the API.\n", "\n", "First, execute the following cell. Once you do that you should see a slider widget directly above a qgrid widget. The slider is hooked up to control the filter for the index column. Next, move the sliders on the slider widget and watch qgrid instance update immediately to reflect the filtering changes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import qgrid\n", "import ipywidgets as widgets\n", "df_scale = pd.DataFrame(np.random.randn(1000, 4), columns=list('ABCD'))\n", "q_scale = qgrid.show_grid(df_scale, show_toolbar=False)\n", "\n", "int_range = widgets.IntRangeSlider(\n", " value=[0, 999],\n", " min=0,\n", " max=999,\n", " step=1,\n", " description='Test:',\n", " disabled=False,\n", " continuous_update=True,\n", " orientation='horizontal',\n", " readout=True,\n", " readout_format='d',\n", ")\n", "\n", "def on_value_change(change):\n", " q_scale._handle_qgrid_msg_helper({\n", " 'field': \"index\",\n", " 'filter_info': {\n", " 'field': \"index\",\n", " 'max': change['new'][1],\n", " 'min': change['new'][0],\n", " 'type': \"slider\"\n", " },\n", " 'type': \"filter_changed\"\n", " })\n", "\n", "int_range.observe(on_value_change, names='value')\n", "\n", "widgets.VBox([int_range, q_scale])" ] } ], "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.5" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "1a834c4c69af4c48bed691594ce5e0ad": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.0.0", "model_name": "LayoutModel", "state": {} }, "3fb8c261d3e249248ea7703065634285": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.0.0", "model_name": "LayoutModel", "state": {} }, "7aa5e1ce49fc4d0f9366bea5c684c58d": { "model_module": "qgrid", "model_module_version": "1.0.0-alpha.6", "model_name": "QgridModel", "state": { "_columns": { "A": { "name": "A", "type": "number" }, "B": { "name": "B", "type": "datetime" }, "C": { "name": "C", "type": "number" }, "D": { "name": "D", "type": "integer" }, "E": { "constraints": { "enum": [ "adams", "hamilton", "jefferson", "kennedy", "lincoln", "madison", "roosevelt", "washington" ] }, "name": "E", "ordered": false, "type": "any" }, "F": { "name": "F", "type": "string" }, "G": { "name": "G", "type": "boolean" }, "index": { "name": "index", "type": "integer" } }, "_df_json": "{\"schema\": {\"fields\":[{\"name\":\"index\",\"type\":\"integer\"},{\"name\":\"A\",\"type\":\"number\"},{\"name\":\"B\",\"type\":\"datetime\"},{\"name\":\"C\",\"type\":\"number\"},{\"name\":\"D\",\"type\":\"integer\"},{\"name\":\"E\",\"type\":\"any\",\"constraints\":{\"enum\":[\"adams\",\"hamilton\",\"jefferson\",\"kennedy\",\"lincoln\",\"madison\",\"roosevelt\",\"washington\"]},\"ordered\":false},{\"name\":\"F\",\"type\":\"string\"},{\"name\":\"G\",\"type\":\"boolean\"}],\"primaryKey\":[\"index\"],\"pandas_version\":\"0.20.0\"}, \"data\": [{\"index\":0,\"A\":1.0,\"B\":\"2013-01-01T00:00:00.000Z\",\"C\":-0.09128,\"D\":3,\"E\":\"washington\",\"F\":\"foo\",\"G\":true},{\"index\":1,\"A\":1.0,\"B\":\"2013-01-02T00:00:00.000Z\",\"C\":-0.57373,\"D\":3,\"E\":\"adams\",\"F\":\"bar\",\"G\":false},{\"index\":2,\"A\":1.0,\"B\":\"2013-01-03T00:00:00.000Z\",\"C\":0.4723,\"D\":3,\"E\":\"washington\",\"F\":\"buzz\",\"G\":false},{\"index\":3,\"A\":1.0,\"B\":\"2013-01-04T00:00:00.000Z\",\"C\":-0.09316,\"D\":3,\"E\":\"madison\",\"F\":\"bippity\",\"G\":false},{\"index\":4,\"A\":1.0,\"B\":\"2013-01-05T00:00:00.000Z\",\"C\":-0.20181,\"D\":3,\"E\":\"lincoln\",\"F\":\"boppity\",\"G\":false},{\"index\":5,\"A\":1.0,\"B\":\"2013-01-06T00:00:00.000Z\",\"C\":1.7561,\"D\":3,\"E\":\"jefferson\",\"F\":\"foo\",\"G\":true},{\"index\":6,\"A\":1.0,\"B\":\"2013-01-07T00:00:00.000Z\",\"C\":1.20064,\"D\":3,\"E\":\"hamilton\",\"F\":\"foo\",\"G\":true},{\"index\":7,\"A\":1.0,\"B\":\"2013-01-08T00:00:00.000Z\",\"C\":0.98097,\"D\":3,\"E\":\"roosevelt\",\"F\":\"bar\",\"G\":false},{\"index\":8,\"A\":1.0,\"B\":\"2013-01-09T00:00:00.000Z\",\"C\":0.8321,\"D\":3,\"E\":\"kennedy\",\"F\":\"zoo\",\"G\":false}]}", "_df_range": [ 0, 100 ], "_interval_columns": [], "_model_module_version": "1.0.0-alpha.6", "_row_count": 9, "_sort_ascending": true, "_sort_field": "", "_view_module_version": "1.0.0-alpha.6", "grid_options": { "autoEdit": false, "defaultColumnWidth": 150, "editable": true, "enableColumnReorder": false, "enableTextSelectionOnCells": true, "explicitInitialization": true, "forceFitColumns": true, "fullWidthRows": true, "rowHeight": 28, "syncColumnCellResize": true }, "layout": "IPY_MODEL_1a834c4c69af4c48bed691594ce5e0ad", "precision": 5, "show_toolbar": false } }, "7ec042abef044645a9f8bd38a3f69faf": { "model_module": "qgrid", "model_module_version": "1.0.0-alpha.6", "model_name": "QgridModel", "state": { "_columns": { "A": { "name": "A", "type": "number" }, "B": { "name": "B", "type": "datetime" }, "C": { "name": "C", "type": "number" }, "D": { "name": "D", "type": "integer" }, "E": { "constraints": { "enum": [ "adams", "hamilton", "jefferson", "kennedy", "lincoln", "madison", "roosevelt", "washington" ] }, "name": "E", "ordered": false, "type": "any" }, "F": { "name": "F", "type": "string" }, "G": { "name": "G", "type": "boolean" }, "index": { "name": "index", "type": "integer" } }, "_df_json": "{\"schema\": {\"fields\":[{\"name\":\"index\",\"type\":\"integer\"},{\"name\":\"A\",\"type\":\"number\"},{\"name\":\"B\",\"type\":\"datetime\"},{\"name\":\"C\",\"type\":\"number\"},{\"name\":\"D\",\"type\":\"integer\"},{\"name\":\"E\",\"type\":\"any\",\"constraints\":{\"enum\":[\"adams\",\"hamilton\",\"jefferson\",\"kennedy\",\"lincoln\",\"madison\",\"roosevelt\",\"washington\"]},\"ordered\":false},{\"name\":\"F\",\"type\":\"string\"},{\"name\":\"G\",\"type\":\"boolean\"}],\"primaryKey\":[\"index\"],\"pandas_version\":\"0.20.0\"}, \"data\": [{\"index\":0,\"A\":1.0,\"B\":\"2013-01-01T00:00:00.000Z\",\"C\":0.13177,\"D\":3,\"E\":\"washington\",\"F\":\"foo\",\"G\":true},{\"index\":1,\"A\":1.0,\"B\":\"2013-01-02T00:00:00.000Z\",\"C\":-1.56846,\"D\":3,\"E\":\"adams\",\"F\":\"bar\",\"G\":false},{\"index\":2,\"A\":1.0,\"B\":\"2013-01-03T00:00:00.000Z\",\"C\":1.2584,\"D\":3,\"E\":\"washington\",\"F\":\"buzz\",\"G\":false},{\"index\":3,\"A\":1.0,\"B\":\"2013-01-04T00:00:00.000Z\",\"C\":-1.54316,\"D\":3,\"E\":\"madison\",\"F\":\"bippity\",\"G\":false},{\"index\":4,\"A\":1.0,\"B\":\"2013-01-05T00:00:00.000Z\",\"C\":0.01483,\"D\":3,\"E\":\"lincoln\",\"F\":\"boppity\",\"G\":false},{\"index\":5,\"A\":1.0,\"B\":\"2013-01-06T00:00:00.000Z\",\"C\":-0.26442,\"D\":3,\"E\":\"jefferson\",\"F\":\"foo\",\"G\":true},{\"index\":6,\"A\":1.0,\"B\":\"2013-01-07T00:00:00.000Z\",\"C\":-1.85692,\"D\":3,\"E\":\"hamilton\",\"F\":\"foo\",\"G\":true},{\"index\":7,\"A\":1.0,\"B\":\"2013-01-08T00:00:00.000Z\",\"C\":1.38498,\"D\":3,\"E\":\"roosevelt\",\"F\":\"bar\",\"G\":false},{\"index\":8,\"A\":1.0,\"B\":\"2013-01-09T00:00:00.000Z\",\"C\":0.40905,\"D\":3,\"E\":\"kennedy\",\"F\":\"zoo\",\"G\":false}]}", "_df_range": [ 0, 100 ], "_interval_columns": [], "_model_module_version": "1.0.0-alpha.6", "_row_count": 9, "_sort_ascending": true, "_sort_field": "", "_view_module_version": "1.0.0-alpha.6", "grid_options": { "autoEdit": false, "defaultColumnWidth": 150, "editable": true, "enableColumnReorder": false, "enableTextSelectionOnCells": true, "explicitInitialization": true, "forceFitColumns": true, "fullWidthRows": true, "rowHeight": 28, "syncColumnCellResize": true }, "layout": "IPY_MODEL_8eb3cf5ce7da431db4a3e4e9d3d64511", "precision": 5, "show_toolbar": false } }, "8eb3cf5ce7da431db4a3e4e9d3d64511": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.0.0", "model_name": "LayoutModel", "state": {} }, "fa118ab9f1c840cd83509f56d50ee163": { "model_module": "qgrid", "model_module_version": "1.0.0-alpha.6", "model_name": "QgridModel", "state": { "_columns": { "A": { "name": "A", "type": "number" }, "B": { "name": "B", "type": "datetime" }, "C": { "name": "C", "type": "number" }, "D": { "name": "D", "type": "integer" }, "E": { "constraints": { "enum": [ "adams", "hamilton", "jefferson", "kennedy", "lincoln", "madison", "roosevelt", "washington" ] }, "name": "E", "ordered": false, "type": "any" }, "F": { "name": "F", "type": "string" }, "G": { "name": "G", "type": "boolean" }, "index": { "name": "index", "type": "integer" } }, "_df_json": "{\"schema\": {\"fields\":[{\"name\":\"index\",\"type\":\"integer\"},{\"name\":\"A\",\"type\":\"number\"},{\"name\":\"B\",\"type\":\"datetime\"},{\"name\":\"C\",\"type\":\"number\"},{\"name\":\"D\",\"type\":\"integer\"},{\"name\":\"E\",\"type\":\"any\",\"constraints\":{\"enum\":[\"adams\",\"hamilton\",\"jefferson\",\"kennedy\",\"lincoln\",\"madison\",\"roosevelt\",\"washington\"]},\"ordered\":false},{\"name\":\"F\",\"type\":\"string\"},{\"name\":\"G\",\"type\":\"boolean\"}],\"primaryKey\":[\"index\"],\"pandas_version\":\"0.20.0\"}, \"data\": [{\"index\":0,\"A\":1.0,\"B\":\"2013-01-01T00:00:00.000Z\",\"C\":-0.67408,\"D\":3,\"E\":\"washington\",\"F\":\"foo\",\"G\":true},{\"index\":1,\"A\":1.0,\"B\":\"2013-01-02T00:00:00.000Z\",\"C\":-0.87122,\"D\":3,\"E\":\"adams\",\"F\":\"bar\",\"G\":false},{\"index\":2,\"A\":1.0,\"B\":\"2013-01-03T00:00:00.000Z\",\"C\":0.58722,\"D\":3,\"E\":\"washington\",\"F\":\"buzz\",\"G\":false},{\"index\":3,\"A\":1.0,\"B\":\"2013-01-04T00:00:00.000Z\",\"C\":0.14947,\"D\":3,\"E\":\"madison\",\"F\":\"bippity\",\"G\":false},{\"index\":4,\"A\":1.0,\"B\":\"2013-01-05T00:00:00.000Z\",\"C\":0.89719,\"D\":3,\"E\":\"lincoln\",\"F\":\"boppity\",\"G\":false},{\"index\":5,\"A\":1.0,\"B\":\"2013-01-06T00:00:00.000Z\",\"C\":0.41141,\"D\":3,\"E\":\"jefferson\",\"F\":\"foo\",\"G\":true},{\"index\":6,\"A\":1.0,\"B\":\"2013-01-07T00:00:00.000Z\",\"C\":0.66837,\"D\":3,\"E\":\"hamilton\",\"F\":\"foo\",\"G\":true},{\"index\":7,\"A\":1.0,\"B\":\"2013-01-08T00:00:00.000Z\",\"C\":0.42815,\"D\":3,\"E\":\"roosevelt\",\"F\":\"bar\",\"G\":false},{\"index\":8,\"A\":1.0,\"B\":\"2013-01-09T00:00:00.000Z\",\"C\":-0.49399,\"D\":3,\"E\":\"kennedy\",\"F\":\"zoo\",\"G\":false}]}", "_df_range": [ 0, 100 ], "_interval_columns": [], "_model_module_version": "1.0.0-alpha.6", "_row_count": 9, "_sort_ascending": true, "_sort_field": "", "_view_module_version": "1.0.0-alpha.6", "grid_options": { "autoEdit": false, "defaultColumnWidth": 150, "editable": true, "enableColumnReorder": false, "enableTextSelectionOnCells": true, "explicitInitialization": true, "forceFitColumns": true, "fullWidthRows": true, "rowHeight": 28, "syncColumnCellResize": true }, "layout": "IPY_MODEL_3fb8c261d3e249248ea7703065634285", "precision": 5, "show_toolbar": false } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 2 }