{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "pn.extension('perspective')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``Perspective`` pane provides an interactive visualization component for large, real-time datasets built on the [Perspective](https://perspective.finos.org/) project.\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "For layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n",
    "\n",
    "* **``aggregates``** (dict): Aggregation spec, e.g. {x: \"distinct count\"}\n",
    "* **``columns``** (list): List of displayed columns.\n",
    "* **``expressions``** (list): List of expressions, e.g. `['\"x\"+\"y\"']`\n",
    "* **``filters``** (list): A list of filters, e.g. `[[\"x\", \"<\", 3], [\"y\", \"contains\", \"abc\"]]`.\n",
    "* **``group_by``** (list): List of columns to group by, e.g. `[\"x\", \"y\"]`\n",
    "* **``object``** (dict or pd.DataFrame): The plot data declared as a dictionary of arrays or a DataFrame.\n",
    "* **``selectable``** (bool, default=True): Whether rows are selectable\n",
    "* **``split_by``** (list):  A list of columns to pivot by. e.g. `[\"x\", \"y\"]`\n",
    "* **``sort``** (list): List of sorting specs, e.g. `[[\"x\", \"desc\"]]`\n",
    "* **``plugin``** (str): The name of a plugin to display the data. For example 'datagrid' or 'd3_xy_scatter'.\n",
    "* **``toggle_config``** (bool): Whether to show the config menu.\n",
    "* **``theme``** (str): The theme of the viewer, available options include `'material'`, `'material-dark'`, `'material-dense'`, `'material-dense-dark'`, `'monokai'`, `'solarized'`, `'solarized-dark'` and `'vaporwave'`\n",
    "\n",
    "___\n",
    "\n",
    "The `Perspective` pane renders columns of data specified as a dictionary of lists or arrays and pandas DataFrames:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = {'x': [1, 2, 3], 'y': [1, 2, 3]}\n",
    "\n",
    "pn.pane.Perspective(data, width=1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also set the various config settings such as choosing the `columns` to display or the `theme` from Python:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame(np.random.randn(400, 4), columns=list('ABCD')).cumsum()\n",
    "\n",
    "stream_perspective = pn.pane.Perspective(\n",
    "    df, plugin='d3_y_line', columns=['A', 'B', 'C', 'D'], theme='material-dark',\n",
    "    sizing_mode='stretch_width', height=500, margin=0\n",
    ")\n",
    "\n",
    "stream_perspective"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `Perspective` pane also supports `stream` and `patch` methods allowing us to efficiently update the data. The amount of data to keep in the streaming buffer can be controlled via the `rollover` option:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rollover = pn.widgets.IntInput(name='Rollover', value=500)\n",
    "\n",
    "def stream():\n",
    "    data = df.iloc[-1] + np.random.randn(4)\n",
    "    stream_perspective.stream(data, rollover.value)\n",
    "\n",
    "cb = pn.state.add_periodic_callback(stream, 50)\n",
    "\n",
    "pn.Row(cb.param.period, rollover)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively we can also `patch` the data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mixed_df = pd.DataFrame({'A': np.arange(10), 'B': np.random.rand(10), 'C': [f'foo{i}' for i in range(10)]})\n",
    "\n",
    "perspective = pn.pane.Perspective(mixed_df, height=500)\n",
    "\n",
    "perspective"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The easiest way to patch the data is by supplying a dictionary as the patch value. The dictionary should have the following structure:\n",
    "\n",
    "```python\n",
    "{\n",
    "    column: [\n",
    "        (index: int or slice, value),\n",
    "        ...\n",
    "    ],\n",
    "    ...\n",
    "}\n",
    "```\n",
    "    \n",
    "As an example, below we will patch the 'A' and 'C' columns. On the `'A'` column we will replace the 0th row and on the `'C'` column we replace the first two rows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "perspective.patch({'A': [(0, 3)], 'C': [(slice(0, 1), 'bar')]})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Controls\n",
    "\n",
    "The `Perspective` pane exposes a number of options which can be changed from both Python and Javascript try out the effect of these parameters interactively:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Row(perspective.controls(jslink=True), perspective)"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
