|
10 | 10 | "xarray-plotly provides helper functions to combine multiple figures:\n", |
11 | 11 | "\n", |
12 | 12 | "- **`overlay`**: Overlay traces on the same axes\n", |
13 | | - "- **`add_secondary_y`**: Plot with two independent y-axes" |
| 13 | + "- **`add_secondary_y`**: Plot with two independent y-axes\n", |
| 14 | + "- **`subplots`**: Arrange independent figures in a grid\n", |
| 15 | + "- **`slider_to_dropdown`**: Convert animation slider to a dropdown menu" |
14 | 16 | ] |
15 | 17 | }, |
16 | 18 | { |
|
23 | 25 | "import plotly.express as px\n", |
24 | 26 | "import xarray as xr\n", |
25 | 27 | "\n", |
26 | | - "from xarray_plotly import add_secondary_y, config, overlay, xpx\n", |
| 28 | + "from xarray_plotly import add_secondary_y, config, overlay, subplots, xpx\n", |
27 | 29 | "\n", |
28 | 30 | "config.notebook()" |
29 | 31 | ] |
|
440 | 442 | "cell_type": "markdown", |
441 | 443 | "id": "27", |
442 | 444 | "metadata": {}, |
| 445 | + "source": [ |
| 446 | + "## subplots\n", |
| 447 | + "\n", |
| 448 | + "Arrange independent figures side-by-side in a grid. Each figure gets its own\n", |
| 449 | + "subplot cell with axes and title automatically derived from the source figure." |
| 450 | + ] |
| 451 | + }, |
| 452 | + { |
| 453 | + "cell_type": "markdown", |
| 454 | + "id": "28", |
| 455 | + "metadata": {}, |
| 456 | + "source": [ |
| 457 | + "### Different Variables Side by Side" |
| 458 | + ] |
| 459 | + }, |
| 460 | + { |
| 461 | + "cell_type": "code", |
| 462 | + "execution_count": null, |
| 463 | + "id": "29", |
| 464 | + "metadata": {}, |
| 465 | + "outputs": [], |
| 466 | + "source": [ |
| 467 | + "# One figure per variable, arranged in a row\n", |
| 468 | + "us_pop = population.sel(country=\"United States\")\n", |
| 469 | + "us_gdp = gdp_per_capita.sel(country=\"United States\")\n", |
| 470 | + "us_life = life_expectancy.sel(country=\"United States\")\n", |
| 471 | + "\n", |
| 472 | + "pop_fig = xpx(us_pop).bar(title=\"Population\")\n", |
| 473 | + "gdp_fig = xpx(us_gdp).line(title=\"GDP per Capita\")\n", |
| 474 | + "life_fig = xpx(us_life).line(title=\"Life Expectancy\")\n", |
| 475 | + "\n", |
| 476 | + "grid = subplots(pop_fig, gdp_fig, life_fig, cols=3)\n", |
| 477 | + "grid.update_layout(title=\"United States Overview\", height=350, showlegend=False)\n", |
| 478 | + "grid" |
| 479 | + ] |
| 480 | + }, |
| 481 | + { |
| 482 | + "cell_type": "markdown", |
| 483 | + "id": "30", |
| 484 | + "metadata": {}, |
| 485 | + "source": [ |
| 486 | + "### 2x2 Grid\n", |
| 487 | + "\n", |
| 488 | + "Use `cols=2` and pass four figures for a 2x2 layout." |
| 489 | + ] |
| 490 | + }, |
| 491 | + { |
| 492 | + "cell_type": "code", |
| 493 | + "execution_count": null, |
| 494 | + "id": "31", |
| 495 | + "metadata": {}, |
| 496 | + "outputs": [], |
| 497 | + "source": [ |
| 498 | + "# One subplot per country\n", |
| 499 | + "fig_us = xpx(population.sel(country=\"United States\")).bar(title=\"United States\")\n", |
| 500 | + "fig_cn = xpx(population.sel(country=\"China\")).bar(title=\"China\")\n", |
| 501 | + "fig_de = xpx(population.sel(country=\"Germany\")).bar(title=\"Germany\")\n", |
| 502 | + "fig_br = xpx(population.sel(country=\"Brazil\")).bar(title=\"Brazil\")\n", |
| 503 | + "\n", |
| 504 | + "grid = subplots(fig_us, fig_cn, fig_de, fig_br, cols=2)\n", |
| 505 | + "grid.update_layout(height=500, showlegend=False)\n", |
| 506 | + "grid" |
| 507 | + ] |
| 508 | + }, |
| 509 | + { |
| 510 | + "cell_type": "markdown", |
| 511 | + "id": "32", |
| 512 | + "metadata": {}, |
| 513 | + "source": [ |
| 514 | + "### Mixed Chart Types\n", |
| 515 | + "\n", |
| 516 | + "Each cell can use a different chart type. Subplot titles fall back to the\n", |
| 517 | + "y-axis label when no explicit title is set." |
| 518 | + ] |
| 519 | + }, |
| 520 | + { |
| 521 | + "cell_type": "code", |
| 522 | + "execution_count": null, |
| 523 | + "id": "33", |
| 524 | + "metadata": {}, |
| 525 | + "outputs": [], |
| 526 | + "source": [ |
| 527 | + "# No explicit title — subplot titles come from the y-axis label (DataArray name)\n", |
| 528 | + "pop_bar = xpx(us_pop).bar()\n", |
| 529 | + "gdp_line = xpx(us_gdp).line()\n", |
| 530 | + "life_scatter = xpx(us_life).scatter()\n", |
| 531 | + "\n", |
| 532 | + "grid = subplots(pop_bar, gdp_line, life_scatter, cols=3)\n", |
| 533 | + "grid.update_layout(height=350, showlegend=False)\n", |
| 534 | + "grid" |
| 535 | + ] |
| 536 | + }, |
| 537 | + { |
| 538 | + "cell_type": "markdown", |
| 539 | + "id": "34", |
| 540 | + "metadata": {}, |
| 541 | + "source": [ |
| 542 | + "### Limitations\n", |
| 543 | + "\n", |
| 544 | + "`subplots` requires single-panel figures — faceted and animated figures are not supported." |
| 545 | + ] |
| 546 | + }, |
| 547 | + { |
| 548 | + "cell_type": "code", |
| 549 | + "execution_count": null, |
| 550 | + "id": "35", |
| 551 | + "metadata": {}, |
| 552 | + "outputs": [], |
| 553 | + "source": [ |
| 554 | + "# Faceted figure → rejected\n", |
| 555 | + "faceted = xpx(population).line(facet_col=\"country\")\n", |
| 556 | + "try:\n", |
| 557 | + " subplots(faceted)\n", |
| 558 | + "except ValueError as e:\n", |
| 559 | + " print(f\"ValueError: {e}\")\n", |
| 560 | + "\n", |
| 561 | + "# Animated figure → rejected\n", |
| 562 | + "animated = xpx(population).bar(animation_frame=\"country\")\n", |
| 563 | + "try:\n", |
| 564 | + " subplots(animated)\n", |
| 565 | + "except ValueError as e:\n", |
| 566 | + " print(f\"ValueError: {e}\")" |
| 567 | + ] |
| 568 | + }, |
| 569 | + { |
| 570 | + "cell_type": "markdown", |
| 571 | + "id": "36", |
| 572 | + "metadata": {}, |
443 | 573 | "source": [ |
444 | 574 | "---\n", |
445 | 575 | "\n", |
|
450 | 580 | }, |
451 | 581 | { |
452 | 582 | "cell_type": "markdown", |
453 | | - "id": "28", |
| 583 | + "id": "37", |
454 | 584 | "metadata": {}, |
455 | 585 | "source": [ |
456 | 586 | "### overlay: Mismatched Facet Structure\n", |
|
461 | 591 | { |
462 | 592 | "cell_type": "code", |
463 | 593 | "execution_count": null, |
464 | | - "id": "29", |
| 594 | + "id": "38", |
465 | 595 | "metadata": {}, |
466 | 596 | "outputs": [], |
467 | 597 | "source": [ |
|
479 | 609 | }, |
480 | 610 | { |
481 | 611 | "cell_type": "markdown", |
482 | | - "id": "30", |
| 612 | + "id": "39", |
483 | 613 | "metadata": {}, |
484 | 614 | "source": [ |
485 | 615 | "### overlay: Animated Overlay on Static Base\n", |
|
490 | 620 | { |
491 | 621 | "cell_type": "code", |
492 | 622 | "execution_count": null, |
493 | | - "id": "31", |
| 623 | + "id": "40", |
494 | 624 | "metadata": {}, |
495 | 625 | "outputs": [], |
496 | 626 | "source": [ |
|
508 | 638 | }, |
509 | 639 | { |
510 | 640 | "cell_type": "markdown", |
511 | | - "id": "32", |
| 641 | + "id": "41", |
512 | 642 | "metadata": {}, |
513 | 643 | "source": [ |
514 | 644 | "### overlay: Mismatched Animation Frames\n", |
|
519 | 649 | { |
520 | 650 | "cell_type": "code", |
521 | 651 | "execution_count": null, |
522 | | - "id": "33", |
| 652 | + "id": "42", |
523 | 653 | "metadata": {}, |
524 | 654 | "outputs": [], |
525 | 655 | "source": [ |
|
535 | 665 | }, |
536 | 666 | { |
537 | 667 | "cell_type": "markdown", |
538 | | - "id": "34", |
| 668 | + "id": "43", |
539 | 669 | "metadata": {}, |
540 | 670 | "source": [ |
541 | 671 | "### add_secondary_y: Mismatched Facet Structure\n", |
|
546 | 676 | { |
547 | 677 | "cell_type": "code", |
548 | 678 | "execution_count": null, |
549 | | - "id": "35", |
| 679 | + "id": "44", |
550 | 680 | "metadata": {}, |
551 | 681 | "outputs": [], |
552 | 682 | "source": [ |
|
564 | 694 | }, |
565 | 695 | { |
566 | 696 | "cell_type": "markdown", |
567 | | - "id": "36", |
| 697 | + "id": "45", |
568 | 698 | "metadata": {}, |
569 | 699 | "source": [ |
570 | 700 | "### add_secondary_y: Animated Secondary on Static Base\n", |
|
575 | 705 | { |
576 | 706 | "cell_type": "code", |
577 | 707 | "execution_count": null, |
578 | | - "id": "37", |
| 708 | + "id": "46", |
579 | 709 | "metadata": {}, |
580 | 710 | "outputs": [], |
581 | 711 | "source": [ |
|
593 | 723 | }, |
594 | 724 | { |
595 | 725 | "cell_type": "markdown", |
596 | | - "id": "38", |
| 726 | + "id": "47", |
597 | 727 | "metadata": {}, |
598 | 728 | "source": [ |
599 | 729 | "### add_secondary_y: Mismatched Animation Frames" |
|
602 | 732 | { |
603 | 733 | "cell_type": "code", |
604 | 734 | "execution_count": null, |
605 | | - "id": "39", |
| 735 | + "id": "48", |
606 | 736 | "metadata": {}, |
607 | 737 | "outputs": [], |
608 | 738 | "source": [ |
|
618 | 748 | }, |
619 | 749 | { |
620 | 750 | "cell_type": "markdown", |
621 | | - "id": "40", |
| 751 | + "id": "49", |
622 | 752 | "metadata": {}, |
623 | 753 | "source": [ |
624 | 754 | "## Summary\n", |
625 | 755 | "\n", |
626 | 756 | "| Function | Facets | Animation | Static + Animated |\n", |
627 | 757 | "|----------|--------|-----------|-------------------|\n", |
628 | 758 | "| `overlay` | Yes (must match) | Yes (frames must match) | Static overlay on animated base OK |\n", |
629 | | - "| `add_secondary_y` | Yes (must match) | Yes (frames must match) | Static secondary on animated base OK |" |
| 759 | + "| `add_secondary_y` | Yes (must match) | Yes (frames must match) | Static secondary on animated base OK |\n", |
| 760 | + "| `subplots` | No (single-panel only) | No | N/A |" |
630 | 761 | ] |
631 | 762 | } |
632 | 763 | ], |
|
0 commit comments