|
472 | 472 | "cell_type": "markdown", |
473 | 473 | "id": "29", |
474 | 474 | "metadata": {}, |
| 475 | + "source": [ |
| 476 | + "### Multi-Trace + Facets\n", |
| 477 | + "\n", |
| 478 | + "Both axes carry multiple traces *within* each facet. This combines facet\n", |
| 479 | + "structure, multi-trace legendgroups, and the secondary y-axis layout." |
| 480 | + ] |
| 481 | + }, |
| 482 | + { |
| 483 | + "cell_type": "code", |
| 484 | + "execution_count": null, |
| 485 | + "id": "30", |
| 486 | + "metadata": {}, |
| 487 | + "outputs": [], |
| 488 | + "source": [ |
| 489 | + "# Synthetic 3D data: month x sensor x site, on two scales (Temperature vs Power).\n", |
| 490 | + "# facet_col=\"site\" splits into facets; the \"sensor\" dimension produces multiple\n", |
| 491 | + "# traces within each facet, identically named on both axes.\n", |
| 492 | + "import numpy as np\n", |
| 493 | + "\n", |
| 494 | + "rng = np.random.default_rng(0)\n", |
| 495 | + "months = np.arange(1, 13)\n", |
| 496 | + "sensors = [\"A\", \"B\", \"C\"]\n", |
| 497 | + "sites = [\"North\", \"South\"]\n", |
| 498 | + "\n", |
| 499 | + "temp_da = xr.DataArray(\n", |
| 500 | + " 20 + rng.standard_normal((12, 3, 2)) * 5,\n", |
| 501 | + " dims=[\"month\", \"sensor\", \"site\"],\n", |
| 502 | + " coords={\"month\": months, \"sensor\": sensors, \"site\": sites},\n", |
| 503 | + " name=\"Temperature (°C)\",\n", |
| 504 | + ")\n", |
| 505 | + "power_da = xr.DataArray(\n", |
| 506 | + " 400 + rng.standard_normal((12, 3, 2)) * 80,\n", |
| 507 | + " dims=[\"month\", \"sensor\", \"site\"],\n", |
| 508 | + " coords={\"month\": months, \"sensor\": sensors, \"site\": sites},\n", |
| 509 | + " name=\"Power (W)\",\n", |
| 510 | + ")\n", |
| 511 | + "\n", |
| 512 | + "temp_fig = xpx(temp_da).line(facet_col=\"site\", markers=True)\n", |
| 513 | + "power_fig = xpx(power_da).line(facet_col=\"site\")\n", |
| 514 | + "\n", |
| 515 | + "combined = add_secondary_y(temp_fig, power_fig)\n", |
| 516 | + "combined.update_layout(title=\"Sensor Temperature (left) vs Power (right) — faceted by site\")\n", |
| 517 | + "combined" |
| 518 | + ] |
| 519 | + }, |
| 520 | + { |
| 521 | + "cell_type": "markdown", |
| 522 | + "id": "31", |
| 523 | + "metadata": {}, |
| 524 | + "source": [ |
| 525 | + "### Composing `overlay` with `add_secondary_y`\n", |
| 526 | + "\n", |
| 527 | + "`add_secondary_y` accepts any base figure — including one already produced by `overlay`. Useful when you want to overlay a trend on the primary axis, then bring in a second variable on a secondary axis." |
| 528 | + ] |
| 529 | + }, |
| 530 | + { |
| 531 | + "cell_type": "code", |
| 532 | + "execution_count": null, |
| 533 | + "id": "32", |
| 534 | + "metadata": {}, |
| 535 | + "outputs": [], |
| 536 | + "source": [ |
| 537 | + "# Primary axis: GOOG daily price + 20-day moving average (via overlay).\n", |
| 538 | + "# Secondary axis: AAPL daily price for comparison.\n", |
| 539 | + "goog = stocks.sel(company=\"GOOG\")\n", |
| 540 | + "goog_ma = goog.rolling(date=20, center=True).mean()\n", |
| 541 | + "goog_ma.name = \"GOOG 20-day MA\"\n", |
| 542 | + "\n", |
| 543 | + "price_fig = xpx(goog).scatter()\n", |
| 544 | + "price_fig.update_traces(marker={\"size\": 4, \"opacity\": 0.5}, name=\"GOOG Daily\")\n", |
| 545 | + "ma_fig = xpx(goog_ma).line()\n", |
| 546 | + "ma_fig.update_traces(line={\"color\": \"red\", \"width\": 2}, name=\"GOOG 20-day MA\")\n", |
| 547 | + "\n", |
| 548 | + "base = overlay(price_fig, ma_fig)\n", |
| 549 | + "\n", |
| 550 | + "aapl = stocks.sel(company=\"AAPL\")\n", |
| 551 | + "aapl_fig = xpx(aapl).line()\n", |
| 552 | + "aapl_fig.update_traces(line={\"color\": \"green\", \"width\": 2}, name=\"AAPL\")\n", |
| 553 | + "\n", |
| 554 | + "combined = add_secondary_y(base, aapl_fig, secondary_y_title=\"AAPL Price\")\n", |
| 555 | + "combined.update_layout(title=\"GOOG (left, raw + MA) vs AAPL (right)\")\n", |
| 556 | + "combined" |
| 557 | + ] |
| 558 | + }, |
| 559 | + { |
| 560 | + "cell_type": "markdown", |
| 561 | + "id": "33", |
| 562 | + "metadata": {}, |
475 | 563 | "source": [ |
476 | 564 | "## subplots\n", |
477 | 565 | "\n", |
|
481 | 569 | }, |
482 | 570 | { |
483 | 571 | "cell_type": "markdown", |
484 | | - "id": "30", |
| 572 | + "id": "34", |
485 | 573 | "metadata": {}, |
486 | 574 | "source": [ |
487 | 575 | "### Different Variables Side by Side" |
|
490 | 578 | { |
491 | 579 | "cell_type": "code", |
492 | 580 | "execution_count": null, |
493 | | - "id": "31", |
| 581 | + "id": "35", |
494 | 582 | "metadata": {}, |
495 | 583 | "outputs": [], |
496 | 584 | "source": [ |
|
510 | 598 | }, |
511 | 599 | { |
512 | 600 | "cell_type": "markdown", |
513 | | - "id": "32", |
| 601 | + "id": "36", |
514 | 602 | "metadata": {}, |
515 | 603 | "source": [ |
516 | 604 | "### 2x2 Grid\n", |
|
521 | 609 | { |
522 | 610 | "cell_type": "code", |
523 | 611 | "execution_count": null, |
524 | | - "id": "33", |
| 612 | + "id": "37", |
525 | 613 | "metadata": {}, |
526 | 614 | "outputs": [], |
527 | 615 | "source": [ |
|
538 | 626 | }, |
539 | 627 | { |
540 | 628 | "cell_type": "markdown", |
541 | | - "id": "34", |
| 629 | + "id": "38", |
542 | 630 | "metadata": {}, |
543 | 631 | "source": [ |
544 | 632 | "### Mixed Chart Types\n", |
|
550 | 638 | { |
551 | 639 | "cell_type": "code", |
552 | 640 | "execution_count": null, |
553 | | - "id": "35", |
| 641 | + "id": "39", |
554 | 642 | "metadata": {}, |
555 | 643 | "outputs": [], |
556 | 644 | "source": [ |
|
566 | 654 | }, |
567 | 655 | { |
568 | 656 | "cell_type": "markdown", |
569 | | - "id": "36", |
| 657 | + "id": "40", |
570 | 658 | "metadata": {}, |
571 | 659 | "source": [ |
572 | 660 | "### With Facets\n", |
|
577 | 665 | { |
578 | 666 | "cell_type": "code", |
579 | 667 | "execution_count": null, |
580 | | - "id": "37", |
| 668 | + "id": "41", |
581 | 669 | "metadata": {}, |
582 | 670 | "outputs": [], |
583 | 671 | "source": [ |
|
592 | 680 | }, |
593 | 681 | { |
594 | 682 | "cell_type": "markdown", |
595 | | - "id": "38", |
| 683 | + "id": "42", |
596 | 684 | "metadata": {}, |
597 | 685 | "source": [ |
598 | 686 | "---\n", |
|
604 | 692 | }, |
605 | 693 | { |
606 | 694 | "cell_type": "markdown", |
607 | | - "id": "39", |
| 695 | + "id": "43", |
608 | 696 | "metadata": {}, |
609 | 697 | "source": [ |
610 | 698 | "### overlay: Mismatched Facet Structure\n", |
|
615 | 703 | { |
616 | 704 | "cell_type": "code", |
617 | 705 | "execution_count": null, |
618 | | - "id": "40", |
| 706 | + "id": "44", |
619 | 707 | "metadata": {}, |
620 | 708 | "outputs": [], |
621 | 709 | "source": [ |
|
633 | 721 | }, |
634 | 722 | { |
635 | 723 | "cell_type": "markdown", |
636 | | - "id": "41", |
| 724 | + "id": "45", |
637 | 725 | "metadata": {}, |
638 | 726 | "source": [ |
639 | 727 | "### overlay: Animated Overlay on Static Base\n", |
|
644 | 732 | { |
645 | 733 | "cell_type": "code", |
646 | 734 | "execution_count": null, |
647 | | - "id": "42", |
| 735 | + "id": "46", |
648 | 736 | "metadata": {}, |
649 | 737 | "outputs": [], |
650 | 738 | "source": [ |
|
662 | 750 | }, |
663 | 751 | { |
664 | 752 | "cell_type": "markdown", |
665 | | - "id": "43", |
| 753 | + "id": "47", |
666 | 754 | "metadata": {}, |
667 | 755 | "source": [ |
668 | 756 | "### overlay: Mismatched Animation Frames\n", |
|
673 | 761 | { |
674 | 762 | "cell_type": "code", |
675 | 763 | "execution_count": null, |
676 | | - "id": "44", |
| 764 | + "id": "48", |
677 | 765 | "metadata": {}, |
678 | 766 | "outputs": [], |
679 | 767 | "source": [ |
|
689 | 777 | }, |
690 | 778 | { |
691 | 779 | "cell_type": "markdown", |
692 | | - "id": "45", |
| 780 | + "id": "49", |
693 | 781 | "metadata": {}, |
694 | 782 | "source": [ |
695 | 783 | "### add_secondary_y: Mismatched Facet Structure\n", |
|
700 | 788 | { |
701 | 789 | "cell_type": "code", |
702 | 790 | "execution_count": null, |
703 | | - "id": "46", |
| 791 | + "id": "50", |
704 | 792 | "metadata": {}, |
705 | 793 | "outputs": [], |
706 | 794 | "source": [ |
|
718 | 806 | }, |
719 | 807 | { |
720 | 808 | "cell_type": "markdown", |
721 | | - "id": "47", |
| 809 | + "id": "51", |
722 | 810 | "metadata": {}, |
723 | 811 | "source": [ |
724 | 812 | "### add_secondary_y: Animated Secondary on Static Base\n", |
|
729 | 817 | { |
730 | 818 | "cell_type": "code", |
731 | 819 | "execution_count": null, |
732 | | - "id": "48", |
| 820 | + "id": "52", |
733 | 821 | "metadata": {}, |
734 | 822 | "outputs": [], |
735 | 823 | "source": [ |
|
747 | 835 | }, |
748 | 836 | { |
749 | 837 | "cell_type": "markdown", |
750 | | - "id": "49", |
| 838 | + "id": "53", |
751 | 839 | "metadata": {}, |
752 | 840 | "source": [ |
753 | 841 | "### add_secondary_y: Mismatched Animation Frames" |
|
756 | 844 | { |
757 | 845 | "cell_type": "code", |
758 | 846 | "execution_count": null, |
759 | | - "id": "50", |
| 847 | + "id": "54", |
760 | 848 | "metadata": {}, |
761 | 849 | "outputs": [], |
762 | 850 | "source": [ |
|
772 | 860 | }, |
773 | 861 | { |
774 | 862 | "cell_type": "markdown", |
775 | | - "id": "51", |
| 863 | + "id": "55", |
776 | 864 | "metadata": {}, |
777 | 865 | "source": [ |
778 | 866 | "## Summary\n", |
|
0 commit comments