|
12 | 12 | "import holoviews as hv\n", |
13 | 13 | "from holoviews import opts\n", |
14 | 14 | "from holoviews.operation import gridmatrix\n", |
15 | | - "from holoviews.operation.element import histogram\n", |
| 15 | + "from holoviews.operation.element import categorical_agg, histogram\n", |
16 | 16 | "from holoviews.selection import link_selections\n", |
17 | 17 | "from holoviews.util.transform import dim\n", |
18 | 18 | "\n", |
|
475 | 475 | "mpg_ls.unlink(w_accel_scatter)" |
476 | 476 | ] |
477 | 477 | }, |
| 478 | + { |
| 479 | + "cell_type": "markdown", |
| 480 | + "metadata": {}, |
| 481 | + "source": [ |
| 482 | + "### Categorical Dimensions with `categorical_agg`\n", |
| 483 | + "\n", |
| 484 | + "The `histogram` operation only works with numeric dimensions. For categorical \n", |
| 485 | + "dimensions, use the built-in `categorical_agg` operation, which aggregates \n", |
| 486 | + "a categorical dimension into a `Bars` element while preserving data lineage \n", |
| 487 | + "for `link_selections`.\n" |
| 488 | + ] |
| 489 | + }, |
| 490 | + { |
| 491 | + "cell_type": "markdown", |
| 492 | + "metadata": {}, |
| 493 | + "source": [ |
| 494 | + "#### Why `categorical_agg` works with `link_selections`\n", |
| 495 | + "\n", |
| 496 | + "`categorical_agg` is an `Operation` subclass, so it remembers its source \n", |
| 497 | + "element. When a selection is made, `link_selections` traces back through the \n", |
| 498 | + "operation to the source (e.g. `Points`), applies the filter there, then \n", |
| 499 | + "re-runs `categorical_agg` on the filtered subset — re-aggregating correctly.\n", |
| 500 | + "\n", |
| 501 | + "Pre-aggregated `hv.Bars(df.groupby(...))` does **not** work because it has \n", |
| 502 | + "no source reference.\n" |
| 503 | + ] |
| 504 | + }, |
| 505 | + { |
| 506 | + "cell_type": "code", |
| 507 | + "execution_count": null, |
| 508 | + "metadata": {}, |
| 509 | + "outputs": [], |
| 510 | + "source": [ |
| 511 | + "hv.extension('bokeh')\n", |
| 512 | + "\n", |
| 513 | + "rng = np.random.default_rng(42)\n", |
| 514 | + "df = pd.DataFrame({\n", |
| 515 | + " 'x': rng.normal(0, 1, 200),\n", |
| 516 | + " 'y': rng.normal(0, 1, 200),\n", |
| 517 | + " 'category': rng.choice(['A', 'B', 'C', 'D'], 200),\n", |
| 518 | + "})\n", |
| 519 | + "\n", |
| 520 | + "ls = hv.link_selections.instance()\n", |
| 521 | + "points = hv.Points(df, kdims=['x', 'y'], vdims=['category'])\n", |
| 522 | + "\n", |
| 523 | + "bars_count = categorical_agg(points, dimension='category')\n", |
| 524 | + "bars_mean = categorical_agg(points, dimension='category', value_dimension='y', function=np.mean)\n", |
| 525 | + "hist_x = histogram(points, dimension='x', num_bins=20)\n", |
| 526 | + "\n", |
| 527 | + "ls(points) + ls(bars_count) + ls(bars_mean) + ls(hist_x)" |
| 528 | + ] |
| 529 | + }, |
| 530 | + { |
| 531 | + "cell_type": "markdown", |
| 532 | + "metadata": {}, |
| 533 | + "source": [ |
| 534 | + "### Supported aggregation functions\n", |
| 535 | + "\n", |
| 536 | + "| Call | Result |\n", |
| 537 | + "|------|--------|\n", |
| 538 | + "| `categorical_agg(points, dimension='cat')` | Count per category |\n", |
| 539 | + "| `categorical_agg(points, dimension='cat', value_dimension='y', function=np.sum)` | Sum of `y` per category |\n", |
| 540 | + "| `categorical_agg(points, dimension='cat', value_dimension='y', function=np.mean)` | Mean of `y` |\n", |
| 541 | + "| `categorical_agg(points, dimension='cat', value_dimension='y', function=np.std)` | Std deviation |\n", |
| 542 | + "\n", |
| 543 | + "You can also click individual bars to cross-filter (tap-to-select).\n" |
| 544 | + ] |
| 545 | + }, |
478 | 546 | { |
479 | 547 | "cell_type": "markdown", |
480 | 548 | "metadata": {}, |
|
558 | 626 | "\n", |
559 | 627 | "<tr><td class=\"sG\">Area</td><td class=\"sY\">Yes</td><td class=\"sY\">Yes</td><td class=\"sY\">Yes</td>\n", |
560 | 628 | " <td class=\"sN\">No </td></tr>\n", |
561 | | - "<tr><td class=\"sG\">Bars</td><td class=\"sN\">No </td><td class=\"sN\">No </td><td class=\"sN\">No </td>\n", |
562 | | - " <td class=\"sN\">No </td><td>Complicated to support stacked and multi-level bars</td></tr>\n", |
| 629 | + "<tr><td class=\"sG\">Bars</td><td class=\"sN\">Yes </td><td class=\"sN\">Yes </td><td class=\"sN\">No </td>\n", |
| 630 | + " <td class=\"sN\">No </td><td>Use categorical_agg with link_selections for tap-to-select on categorical bars</td></tr>\n", |
563 | 631 | "<tr><td class=\"sG\">Bivariate</td><td class=\"sY\">Yes</td><td class=\"sY\">Yes</td><td class=\"sY\">Yes</td>\n", |
564 | 632 | " <td class=\"sY\">Yes </td></tr>\n", |
565 | 633 | "<tr><td class=\"sG\">BoxWhisker</td><td class=\"sY\">Yes</td><td class=\"sY\">Yes</td><td class=\"sY\">Yes</td>\n", |
|
0 commit comments