How to make a plume plot¶
In this notebook we describe how to make a plume plot using pandas-OpenSCM. If you don't know what a plume plot is, read on, it will be shown soon.
Imports¶
import traceback
import matplotlib.pyplot as plt
import matplotlib.units
import numpy as np
import openscm_units
from pandas_openscm import register_pandas_accessors
from pandas_openscm.plotting import PlumePlotter
from pandas_openscm.testing import create_test_df
Setup¶
# Register the openscm accessor for pandas objects
# (we don't do this on import
# as we have had bad experiences with implicit behaviour like that)
register_pandas_accessors()
Basics¶
Imagine we start with some data that has multiple realisations. For example, multiple runs from a simple climate model.
df_basic = create_test_df(
variables=(("Warming", "K"),),
n_scenarios=5,
n_runs=10,
timepoints=np.arange(1950.0, 1965.0),
)
df_basic
| 1950.0 | 1951.0 | 1952.0 | 1953.0 | 1954.0 | 1955.0 | 1956.0 | 1957.0 | 1958.0 | 1959.0 | 1960.0 | 1961.0 | 1962.0 | 1963.0 | 1964.0 | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| scenario | variable | run | unit | |||||||||||||||
| scenario_0 | Warming | 0 | K | 0.613001 | 1.177778 | 2.639132 | 3.771635 | 4.794293 | 6.078960 | 7.069806 | 8.315988 | 9.399471 | 9.897312 | 11.661044 | 12.413763 | 13.289298 | 14.869252 | 15.996323 |
| 1 | K | 0.507109 | 1.707119 | 2.827007 | 3.586881 | 5.067869 | 6.584367 | 7.693056 | 8.082170 | 9.400568 | 10.183550 | 11.482746 | 12.386337 | 13.852699 | 14.938290 | 16.152885 | ||
| 2 | K | 0.725287 | 1.807198 | 2.571881 | 4.313736 | 5.103240 | 6.358802 | 7.701212 | 8.592074 | 9.863423 | 10.924851 | 11.756912 | 13.548969 | 14.745908 | 15.779384 | 16.887180 | ||
| 3 | K | 0.939430 | 1.410789 | 2.933224 | 4.160713 | 5.041899 | 6.642079 | 8.035187 | 8.773160 | 10.777859 | 11.136089 | 13.049357 | 14.441658 | 14.760399 | 16.307839 | 18.009682 | ||
| 4 | K | 0.343798 | 1.285534 | 2.942386 | 3.941443 | 5.800850 | 7.284551 | 8.163030 | 9.887792 | 10.658262 | 12.016660 | 13.278992 | 14.202643 | 15.457940 | 16.690155 | 18.096287 | ||
| 5 | K | 0.494452 | 1.489901 | 2.867305 | 4.167583 | 5.833121 | 7.480796 | 8.803628 | 9.924140 | 10.823703 | 12.288370 | 13.382533 | 14.614353 | 15.981720 | 18.023691 | 18.697379 | ||
| 6 | K | 0.547448 | 1.665206 | 3.387052 | 4.277246 | 6.474327 | 7.293758 | 8.457127 | 9.977355 | 11.697617 | 13.151853 | 14.280795 | 15.244328 | 16.980621 | 18.793798 | 19.956588 | ||
| 7 | K | 0.694846 | 2.386180 | 2.910271 | 4.976059 | 6.077329 | 7.947228 | 9.028960 | 10.972440 | 12.378229 | 13.239043 | 14.681401 | 16.606468 | 17.650557 | 19.224838 | 20.127452 | ||
| 8 | K | 0.082785 | 1.905625 | 3.430909 | 4.893779 | 6.528051 | 8.170121 | 8.912051 | 10.801076 | 12.575037 | 13.957056 | 15.435732 | 16.577824 | 17.935299 | 19.845680 | 21.660446 | ||
| 9 | K | 0.336046 | 2.148200 | 3.576894 | 5.422742 | 6.569155 | 7.863264 | 9.583262 | 11.360164 | 12.558774 | 14.335934 | 15.587687 | 17.163589 | 19.043382 | 19.901835 | 22.176125 | ||
| scenario_1 | Warming | 0 | K | 0.137022 | 2.099800 | 3.167436 | 5.577501 | 6.783786 | 8.305814 | 9.581257 | 11.750878 | 13.471430 | 14.662399 | 16.166286 | 17.465270 | 19.664487 | 20.895063 | 22.595573 |
| 1 | K | 0.524624 | 2.188810 | 3.546281 | 5.013709 | 6.650308 | 9.155045 | 10.157672 | 12.199955 | 13.320495 | 15.252645 | 16.683685 | 18.645771 | 19.846784 | 21.684928 | 23.645206 | ||
| 2 | K | 0.467495 | 2.001657 | 4.235183 | 5.580622 | 7.434972 | 8.685275 | 10.322881 | 12.162337 | 14.037101 | 16.118122 | 17.397290 | 18.982069 | 20.654880 | 22.042127 | 23.882364 | ||
| 3 | K | 0.371737 | 2.154662 | 4.142613 | 5.768372 | 7.297585 | 9.401659 | 11.072532 | 12.235612 | 13.993098 | 15.837047 | 17.540179 | 19.473653 | 21.071471 | 23.192200 | 25.039546 | ||
| 4 | K | 0.396102 | 2.419546 | 3.973815 | 5.612098 | 7.952405 | 9.561242 | 11.582157 | 12.809158 | 15.017192 | 16.207423 | 18.080781 | 20.563743 | 21.741933 | 23.369584 | 25.191096 | ||
| 5 | K | 0.542514 | 2.618233 | 4.572383 | 6.141276 | 7.849460 | 9.972121 | 11.263896 | 13.581097 | 15.532691 | 17.166413 | 19.238310 | 20.997876 | 22.293325 | 24.050954 | 25.998040 | ||
| 6 | K | 0.336893 | 2.450029 | 3.948849 | 5.836359 | 7.633453 | 9.445672 | 11.498476 | 13.317656 | 15.890374 | 17.864574 | 19.364895 | 21.202575 | 23.619278 | 24.898979 | 26.978780 | ||
| 7 | K | 0.839585 | 1.958817 | 4.246526 | 6.720359 | 8.192690 | 9.965363 | 11.749927 | 14.410125 | 15.809709 | 17.883865 | 20.373981 | 22.108977 | 23.705318 | 25.605862 | 27.326634 | ||
| 8 | K | 0.279715 | 2.090642 | 4.842796 | 6.196894 | 8.750696 | 10.780449 | 12.486235 | 14.255004 | 16.063303 | 18.601797 | 20.331223 | 22.656807 | 24.792117 | 26.513510 | 28.118586 | ||
| 9 | K | 0.122108 | 2.619009 | 4.448614 | 6.275165 | 8.630545 | 10.677253 | 12.628243 | 14.654343 | 17.268378 | 18.724388 | 20.795449 | 22.971560 | 24.740851 | 26.668539 | 29.200870 | ||
| scenario_2 | Warming | 0 | K | 0.341231 | 2.839995 | 5.102848 | 7.105955 | 8.563062 | 11.037305 | 13.046298 | 15.507647 | 16.977202 | 19.050639 | 21.462680 | 23.022983 | 25.375967 | 27.940435 | 30.207767 |
| 1 | K | 0.750207 | 2.930093 | 4.838807 | 6.901187 | 9.426644 | 10.982508 | 13.549941 | 15.598931 | 17.617015 | 20.250401 | 22.167667 | 23.985779 | 26.369439 | 28.348274 | 30.846810 | ||
| 2 | K | 0.706568 | 2.345690 | 5.352904 | 6.581689 | 9.089029 | 11.653589 | 13.577697 | 15.724582 | 17.814642 | 20.641937 | 22.241156 | 24.973589 | 27.141001 | 28.926125 | 30.837766 | ||
| 3 | K | 0.782167 | 2.295027 | 4.562744 | 7.071592 | 9.354637 | 11.551235 | 14.189216 | 16.270952 | 18.753759 | 20.976541 | 23.208387 | 25.596735 | 27.371777 | 29.704296 | 31.742050 | ||
| 4 | K | 0.964998 | 2.875191 | 5.575003 | 7.702039 | 9.750078 | 12.261319 | 14.351704 | 16.295187 | 18.948131 | 21.236690 | 23.168459 | 25.828053 | 28.179051 | 30.736252 | 32.829473 | ||
| 5 | K | 0.942523 | 2.994344 | 4.829784 | 7.804714 | 9.446368 | 12.232037 | 14.319325 | 16.487662 | 19.544499 | 21.297728 | 24.132829 | 26.080546 | 28.623330 | 31.226153 | 33.521916 | ||
| 6 | K | 0.071314 | 2.948947 | 4.922893 | 7.650991 | 10.403741 | 12.165348 | 15.001429 | 17.000590 | 19.472896 | 22.255391 | 24.560684 | 27.133205 | 29.693390 | 31.774274 | 33.768033 | ||
| 7 | K | 0.341943 | 2.655690 | 5.864482 | 8.243918 | 9.855682 | 13.221261 | 14.888624 | 18.074997 | 19.688825 | 22.937625 | 25.143887 | 27.004427 | 30.347513 | 32.586179 | 34.820681 | ||
| 8 | K | 0.124524 | 3.288941 | 5.606735 | 8.289481 | 10.513997 | 13.272379 | 15.073671 | 18.408447 | 20.383299 | 22.778421 | 25.498959 | 28.247781 | 30.562803 | 32.882125 | 35.130388 | ||
| 9 | K | 0.442016 | 3.487269 | 5.857790 | 7.921306 | 10.699065 | 13.330296 | 15.328627 | 18.129848 | 21.075745 | 23.243528 | 25.865325 | 28.490693 | 31.315953 | 33.981955 | 36.656571 | ||
| scenario_3 | Warming | 0 | K | 0.079287 | 3.460091 | 5.307891 | 8.392757 | 10.936726 | 13.252234 | 16.454419 | 18.445239 | 21.805356 | 24.008000 | 26.222522 | 28.741715 | 31.915225 | 34.216913 | 36.882852 |
| 1 | K | 0.240924 | 3.543518 | 5.568438 | 8.132900 | 10.664744 | 14.133846 | 16.222529 | 19.130135 | 21.536478 | 24.201601 | 27.224615 | 29.990365 | 32.740877 | 35.304474 | 37.401267 | ||
| 2 | K | 0.291651 | 3.550977 | 5.601779 | 8.496204 | 11.170428 | 14.362818 | 16.279005 | 19.080621 | 22.631360 | 25.160044 | 27.295701 | 29.883290 | 32.522700 | 35.973317 | 38.413375 | ||
| 3 | K | 0.752270 | 2.923466 | 6.506310 | 8.349568 | 11.505018 | 14.461194 | 16.605152 | 20.096702 | 22.693911 | 25.784566 | 28.248143 | 31.139359 | 34.044567 | 36.352192 | 39.036188 | ||
| 4 | K | 0.277476 | 3.344400 | 6.037203 | 9.410427 | 11.931896 | 14.794745 | 16.880828 | 20.227689 | 23.102539 | 25.740418 | 28.915346 | 31.765059 | 34.385744 | 37.077420 | 39.551346 | ||
| 5 | K | 0.836047 | 2.952568 | 6.648247 | 8.776154 | 11.890571 | 14.300208 | 17.399152 | 20.545841 | 23.165160 | 26.015843 | 29.006638 | 32.112330 | 34.443175 | 37.590736 | 40.759010 | ||
| 6 | K | 0.972376 | 3.789078 | 6.331402 | 9.221703 | 12.100593 | 14.592025 | 18.026154 | 20.882058 | 23.516304 | 26.859933 | 29.151827 | 32.690710 | 35.416387 | 37.916371 | 41.691032 | ||
| 7 | K | 0.167407 | 3.031946 | 5.978482 | 9.778636 | 11.939547 | 15.369516 | 18.595394 | 21.403748 | 23.855695 | 27.627961 | 29.896143 | 33.050724 | 35.542277 | 39.430889 | 42.038639 | ||
| 8 | K | 0.016264 | 3.866278 | 6.700165 | 9.652981 | 12.363396 | 15.993791 | 18.953582 | 22.003328 | 24.483124 | 27.525775 | 31.070546 | 33.262218 | 36.655905 | 39.808362 | 42.442090 | ||
| 9 | K | 0.354983 | 3.962244 | 6.927685 | 9.463852 | 12.712466 | 15.326074 | 18.397617 | 21.928584 | 25.259567 | 28.316235 | 31.190347 | 33.837577 | 37.446983 | 40.568051 | 43.069623 | ||
| scenario_4 | Warming | 0 | K | 0.216562 | 3.219503 | 6.788252 | 9.819599 | 12.994225 | 16.208050 | 19.323881 | 22.466769 | 25.710312 | 28.326848 | 31.922266 | 34.289513 | 37.573034 | 41.388872 | 43.586024 |
| 1 | K | 0.144567 | 3.324932 | 7.192358 | 9.752875 | 12.855154 | 16.252253 | 19.013699 | 22.724026 | 25.495505 | 28.657085 | 32.592624 | 34.971694 | 38.884283 | 41.576166 | 45.129578 | ||
| 2 | K | 0.634955 | 3.957818 | 7.018778 | 9.905585 | 12.969373 | 16.117474 | 20.234106 | 23.132579 | 26.416077 | 29.223617 | 32.914740 | 35.694719 | 38.665793 | 41.955000 | 45.254535 | ||
| 3 | K | 0.599535 | 3.299904 | 6.797172 | 9.959689 | 13.325354 | 16.366750 | 20.182373 | 23.330631 | 26.882358 | 29.496162 | 33.416610 | 36.591183 | 39.379267 | 42.770372 | 45.950414 | ||
| 4 | K | 0.521061 | 4.049997 | 7.532699 | 10.860137 | 13.489963 | 17.377319 | 20.126707 | 23.386960 | 27.282572 | 30.520175 | 33.823740 | 36.836136 | 39.994339 | 43.763153 | 46.881973 | ||
| 5 | K | 0.140555 | 4.259347 | 6.931531 | 10.426431 | 14.063763 | 16.852532 | 20.234102 | 23.668975 | 26.971315 | 30.726738 | 33.977645 | 37.799298 | 41.368374 | 44.131323 | 47.198113 | ||
| 6 | K | 0.196106 | 4.111141 | 6.993011 | 10.671160 | 13.774041 | 17.939874 | 20.763507 | 24.524493 | 27.483137 | 30.954059 | 35.105051 | 37.992048 | 41.756635 | 45.173103 | 48.236695 | ||
| 7 | K | 0.316323 | 4.228454 | 7.417199 | 10.511502 | 14.209567 | 17.420437 | 20.945700 | 24.689902 | 28.551738 | 31.889440 | 35.298608 | 38.706145 | 42.553609 | 45.222365 | 49.126732 | ||
| 8 | K | 0.295185 | 3.750829 | 7.636295 | 11.231076 | 14.106786 | 18.415025 | 21.708649 | 25.373385 | 28.504480 | 32.275121 | 35.702541 | 39.615923 | 42.490830 | 46.657991 | 50.257233 | ||
| 9 | K | 0.604290 | 4.515228 | 7.198125 | 11.449236 | 14.616042 | 18.800278 | 21.681641 | 25.814086 | 28.664040 | 32.893728 | 36.128286 | 40.147700 | 43.660731 | 47.344976 | 50.797526 |
From this data, we can calculate statistics over the runs. For example, different quantiles.
df_basic_quantiles = (
df_basic.openscm.groupby_except("run")
.quantile([0.05, 0.25, 0.5, 0.75, 0.95])
.openscm.fix_index_name_after_groupby_quantile()
)
df_basic_quantiles
| 1950.0 | 1951.0 | 1952.0 | 1953.0 | 1954.0 | 1955.0 | 1956.0 | 1957.0 | 1958.0 | 1959.0 | 1960.0 | 1961.0 | 1962.0 | 1963.0 | 1964.0 | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| scenario | variable | unit | quantile | |||||||||||||||
| scenario_0 | Warming | K | 0.05 | 0.196753 | 1.226268 | 2.602144 | 3.670020 | 4.905716 | 6.204889 | 7.350269 | 8.187388 | 9.399965 | 10.026119 | 11.562980 | 12.398679 | 13.542829 | 14.900319 | 16.066776 |
| 0.25 | 0.381462 | 1.430567 | 2.837081 | 3.996260 | 5.076711 | 6.598795 | 7.784706 | 8.637346 | 10.062132 | 10.977660 | 12.080024 | 13.712387 | 14.749531 | 15.911498 | 17.167806 | |||
| 0.50 | 0.527279 | 1.686162 | 2.921748 | 4.222415 | 5.816986 | 7.289154 | 8.310078 | 9.905966 | 10.800781 | 12.152515 | 13.330762 | 14.528006 | 15.719830 | 17.356923 | 18.396833 | |||
| 0.75 | 0.674385 | 1.881018 | 3.275885 | 4.748768 | 6.375078 | 7.767647 | 8.884945 | 10.595146 | 12.208076 | 13.217245 | 14.581250 | 16.244450 | 17.483073 | 19.117078 | 20.084736 | |||
| 0.95 | 0.843066 | 2.279089 | 3.511201 | 5.221735 | 6.550658 | 8.069819 | 9.333826 | 11.185688 | 12.567719 | 14.165439 | 15.519307 | 16.912884 | 18.544745 | 19.876565 | 21.944069 | |||
| scenario_1 | Warming | K | 0.05 | 0.128819 | 1.978095 | 3.337916 | 5.267416 | 6.710373 | 8.476571 | 9.840644 | 11.936035 | 13.388416 | 14.928010 | 16.399116 | 17.996495 | 19.746520 | 21.250502 | 23.067908 |
| 0.25 | 0.294010 | 2.092932 | 3.955091 | 5.588491 | 7.331932 | 9.216698 | 10.510294 | 12.208870 | 14.004099 | 15.907315 | 17.433012 | 19.104965 | 20.759027 | 22.329645 | 24.171659 | |||
| 0.50 | 0.383919 | 2.171736 | 4.188898 | 5.802365 | 7.741456 | 9.503457 | 11.381186 | 13.063407 | 15.274941 | 16.686918 | 18.659546 | 20.780809 | 22.017629 | 23.710269 | 25.594568 | |||
| 0.75 | 0.510342 | 2.442408 | 4.398092 | 6.182989 | 8.132619 | 9.970432 | 11.707984 | 14.086527 | 15.870208 | 17.879043 | 20.089641 | 21.882376 | 23.683808 | 25.429141 | 27.239671 | |||
| 0.95 | 0.705903 | 2.618660 | 4.721110 | 6.520022 | 8.696628 | 10.734010 | 12.564339 | 14.544445 | 16.726095 | 18.669222 | 20.605788 | 22.829921 | 24.769048 | 26.598776 | 28.713842 | |||
| scenario_2 | Warming | K | 0.05 | 0.095259 | 2.317826 | 4.682912 | 6.725463 | 8.799747 | 11.007167 | 13.272937 | 15.548724 | 17.265118 | 19.590532 | 21.779925 | 23.456241 | 25.823029 | 28.123963 | 30.491267 |
| 0.25 | 0.341409 | 2.701766 | 4.859829 | 7.080182 | 9.372638 | 11.576823 | 13.730577 | 15.861175 | 18.049421 | 20.725588 | 22.472982 | 25.129375 | 27.198695 | 29.120668 | 31.070620 | |||
| 0.50 | 0.574292 | 2.902642 | 5.227876 | 7.676515 | 9.598223 | 12.198693 | 14.335515 | 16.391424 | 19.210513 | 21.267209 | 23.670608 | 25.954299 | 28.401191 | 30.981203 | 33.175695 | |||
| 0.75 | 0.774177 | 2.982995 | 5.598802 | 7.892158 | 10.266726 | 12.981276 | 14.973227 | 17.806395 | 19.652743 | 22.647664 | 24.998086 | 27.101011 | 30.183982 | 32.383203 | 34.557519 | |||
| 0.95 | 0.954885 | 3.398021 | 5.861471 | 8.268978 | 10.615784 | 13.304233 | 15.213897 | 18.283077 | 20.764144 | 23.105872 | 25.700460 | 28.381382 | 30.977036 | 33.487032 | 35.969789 | |||
| scenario_3 | Warming | K | 0.05 | 0.044625 | 2.936562 | 5.425137 | 8.230401 | 10.787136 | 13.648960 | 16.247943 | 18.731161 | 21.657473 | 24.095120 | 26.673464 | 29.255424 | 32.188589 | 34.706315 | 37.116139 |
| 0.25 | 0.185786 | 3.110060 | 5.695955 | 8.418619 | 11.254075 | 14.315861 | 16.492103 | 19.371777 | 22.646998 | 25.305138 | 27.533811 | 30.277614 | 33.066800 | 36.068036 | 38.569079 | |||
| 0.50 | 0.284564 | 3.501804 | 6.184302 | 8.998928 | 11.911233 | 14.526609 | 17.139990 | 20.386765 | 23.133850 | 25.900205 | 28.960992 | 31.938695 | 34.414460 | 37.334078 | 40.155178 | |||
| 0.75 | 0.652948 | 3.729553 | 6.612763 | 9.450496 | 12.060332 | 15.193242 | 18.304751 | 21.273325 | 23.770848 | 27.359315 | 29.710064 | 32.960720 | 35.510804 | 39.052259 | 41.951737 | |||
| 0.95 | 0.911028 | 3.919059 | 6.825301 | 9.722091 | 12.555385 | 15.712868 | 18.792398 | 21.969693 | 24.910168 | 28.006512 | 31.136436 | 33.578666 | 37.090998 | 40.226191 | 42.787234 | |||
| scenario_4 | Warming | K | 0.05 | 0.142360 | 3.255683 | 6.792266 | 9.782901 | 12.906552 | 16.158233 | 19.153281 | 22.582535 | 25.592168 | 28.475454 | 32.223927 | 34.596494 | 38.064775 | 41.473154 | 44.280623 |
| 0.25 | 0.201220 | 3.431406 | 6.946901 | 9.919111 | 13.077007 | 16.280877 | 20.140624 | 23.182092 | 26.532647 | 29.291754 | 33.040207 | 35.918835 | 39.008029 | 42.158843 | 45.428505 | |||
| 0.50 | 0.305754 | 4.003907 | 7.105568 | 10.468966 | 13.632002 | 17.114926 | 20.234104 | 23.527968 | 27.126943 | 30.623456 | 33.900693 | 37.317717 | 40.681357 | 43.947238 | 47.040043 | |||
| 0.75 | 0.579917 | 4.199126 | 7.362431 | 10.812893 | 14.096031 | 17.810014 | 20.900152 | 24.648550 | 28.249144 | 31.655595 | 35.250219 | 38.527621 | 42.307281 | 45.210050 | 48.904222 | |||
| 0.95 | 0.621156 | 4.400082 | 7.589677 | 11.351064 | 14.433128 | 18.626914 | 21.696495 | 25.615771 | 28.613504 | 32.615355 | 35.936701 | 39.908400 | 43.162526 | 47.035833 | 50.554394 |
Then we can make a plume plot, i.e. a plot which shows ranges between quantiles and/or individual quantiles.
df_basic_quantiles.openscm.plot_plume(
quantiles_plumes=(
# The syntax here is:
# (quantile(s), alpha)
# If quantile(s) is a float, we plot a line.
# If quantile(s) is a tuple, we plot a plume between the given quantiles.
(0.5, 0.8),
((0.25, 0.75), 0.5),
((0.05, 0.95), 0.3),
),
)
<Axes: xlabel='time', ylabel='K'>
With plot_plume_after_calculating_quantiles, you don't have to calculate the quantiles first yourself.
df_basic.openscm.plot_plume_after_calculating_quantiles(
quantile_over="run",
quantiles_plumes=(
# Plot two quantiles as lines, rather than just one
(0.25, 0.4),
(0.75, 0.8),
((0.1, 0.9), 0.3),
),
)
<Axes: xlabel='time', ylabel='K'>
By default, we try and set the x- and y-labels. It is possible to provide your own values.
df_basic_quantiles.openscm.plot_plume(
quantiles_plumes=(((0.05, 0.95), 0.3),),
x_label="Year",
y_label="Warming",
)
<Axes: xlabel='Year', ylabel='Warming'>
Beyond splitting the data into different colours, it is also possible to split along another dimension/facet too, using the style variable.
df_multi_facet = create_test_df(
variables=(("Warming land", "K"), ("Warming ocean", "K")),
n_scenarios=5,
n_runs=10,
timepoints=np.arange(1850.0, 2024.0),
)
df_multi_facet.openscm.plot_plume_after_calculating_quantiles(
style_var="variable",
quantile_over="run",
quantiles_plumes=(
(0.5, 0.8),
((0.1, 0.9), 0.3),
),
)
<Axes: xlabel='time', ylabel='K'>
Summary so far¶
The functionality shown up to here is the key functionality. If all you need to do is basic plots, this is all you need. If you are looking to do fancier customisation of plots, read on.
Advanced topics¶
Fine-grained control¶
Almost every aspect of the plot can be altered as you wish. Below we show the full range of options that can be passed. These can be passed via the accessors we have used in these docs or directly via the underlying functions.
Firstly, we set up some specific data. For example, data without quantiles but rather very specific percentiles (e.g. someone sent you pre-processed data).
df_advanced = create_test_df(
variables=(("variable_1", "tCO2"), ("variable_2", "dtCO2")),
n_scenarios=3,
n_runs=10,
timepoints=np.arange(2025.0, 2150.0),
)
# Imagine that we don't have quantiles, rather very specific percentiles
# (e.g. someone sent you pre-processed data).
plot_df = (
df_advanced.openscm.groupby_except("run")
.quantile([0.1685321, 0.5, 0.8355321])
.openscm.fix_index_name_after_groupby_quantile(new_name="percentile")
.reset_index(["unit", "percentile"])
)
plot_df["percentile"] *= 100.0
plot_df = plot_df.rename({"unit": "units"}, axis="columns")
plot_df = plot_df.set_index(["units", "percentile"], append=True)
plot_df.columns = plot_df.columns.astype(float)
plot_df
| 2025.0 | 2026.0 | 2027.0 | 2028.0 | 2029.0 | 2030.0 | 2031.0 | 2032.0 | 2033.0 | 2034.0 | ... | 2140.0 | 2141.0 | 2142.0 | 2143.0 | 2144.0 | 2145.0 | 2146.0 | 2147.0 | 2148.0 | 2149.0 | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| scenario | variable | units | percentile | |||||||||||||||||||||
| scenario_0 | variable_1 | tCO2 | 16.85321 | 0.310320 | 0.288907 | 0.482136 | 0.569430 | 0.695012 | 0.757626 | 1.083911 | 1.165265 | 1.498234 | 1.294744 | ... | 14.821152 | 15.455206 | 15.760701 | 15.469948 | 15.672881 | 16.075069 | 15.752711 | 16.275886 | 16.610462 | 16.166878 |
| 50.00000 | 0.674880 | 0.712712 | 0.709954 | 0.898478 | 1.033531 | 1.011649 | 1.313094 | 1.616796 | 1.864675 | 1.811708 | ... | 17.037771 | 17.035194 | 17.514604 | 16.946757 | 17.802894 | 17.492952 | 17.658413 | 18.106206 | 18.226231 | 18.185782 | |||
| 83.55321 | 0.854523 | 0.904582 | 0.959831 | 1.317971 | 1.286619 | 1.177184 | 1.418904 | 1.705020 | 2.006473 | 2.202070 | ... | 18.887529 | 18.402448 | 18.694518 | 19.310921 | 19.456054 | 18.941321 | 19.521242 | 19.811127 | 19.466965 | 19.937324 | |||
| variable_2 | dtCO2 | 16.85321 | 0.302957 | 0.387141 | 0.459496 | 0.675347 | 1.006609 | 1.201174 | 1.220426 | 1.566340 | 1.702491 | 1.819125 | ... | 20.830377 | 21.184669 | 21.114655 | 21.303464 | 21.543527 | 21.664355 | 22.033881 | 21.689413 | 22.419825 | 22.194241 | |
| 50.00000 | 0.555354 | 0.651807 | 0.683842 | 1.066308 | 1.232997 | 1.470834 | 1.421417 | 1.798710 | 2.086714 | 2.061682 | ... | 22.308720 | 22.930045 | 22.869360 | 22.767028 | 22.708122 | 23.082380 | 23.649717 | 24.125267 | 23.748289 | 23.892310 | |||
| 83.55321 | 0.906403 | 0.979713 | 0.949731 | 1.361930 | 1.441947 | 1.777560 | 1.894654 | 2.022628 | 2.330572 | 2.613032 | ... | 24.058371 | 24.254213 | 24.112078 | 24.715698 | 24.656470 | 24.751812 | 25.141757 | 25.473301 | 25.696851 | 25.796558 | |||
| scenario_1 | variable_1 | tCO2 | 16.85321 | 0.251701 | 0.574737 | 0.543749 | 1.059270 | 1.050556 | 1.474007 | 1.739063 | 1.862321 | 1.970182 | 2.217324 | ... | 26.061449 | 26.495188 | 26.674590 | 27.014087 | 27.225425 | 27.340491 | 27.551066 | 27.935007 | 27.856623 | 28.557825 |
| 50.00000 | 0.434024 | 0.764261 | 0.755842 | 1.228024 | 1.278893 | 1.734529 | 1.927278 | 2.165855 | 2.184824 | 2.493997 | ... | 27.642353 | 28.297843 | 28.419959 | 29.052115 | 28.757489 | 29.335713 | 29.702088 | 29.496058 | 29.833826 | 29.848152 | |||
| 83.55321 | 0.743614 | 0.994439 | 1.232796 | 1.576649 | 1.666732 | 1.806245 | 2.208540 | 2.521923 | 2.449943 | 3.015444 | ... | 29.548141 | 30.010090 | 30.200372 | 30.273589 | 30.561431 | 30.668354 | 31.264700 | 31.179454 | 31.488881 | 31.964185 | |||
| variable_2 | dtCO2 | 16.85321 | 0.200997 | 0.601945 | 0.987960 | 1.134255 | 1.347458 | 1.518771 | 1.912630 | 2.200651 | 2.383933 | 2.802727 | ... | 31.840437 | 32.454673 | 32.313063 | 32.681615 | 33.014473 | 33.116086 | 33.593373 | 33.428285 | 33.656269 | 34.394848 | |
| 50.00000 | 0.602263 | 0.697916 | 1.171598 | 1.382828 | 1.551927 | 1.756571 | 2.073596 | 2.441187 | 2.536216 | 3.055255 | ... | 33.141713 | 33.483795 | 34.018056 | 33.860333 | 34.495374 | 34.790350 | 35.361781 | 35.535606 | 35.433115 | 35.979772 | |||
| 83.55321 | 0.918050 | 0.984899 | 1.420273 | 1.712747 | 1.635293 | 2.072037 | 2.406077 | 2.895561 | 3.097791 | 3.421152 | ... | 34.786063 | 35.397622 | 35.567852 | 35.996163 | 36.161602 | 36.549924 | 36.833136 | 36.882300 | 37.186387 | 37.856359 | |||
| scenario_2 | variable_1 | tCO2 | 16.85321 | 0.416879 | 0.513886 | 0.844456 | 1.213876 | 1.533360 | 1.851706 | 2.285464 | 2.578437 | 2.844378 | 3.173100 | ... | 37.416540 | 37.642160 | 37.700628 | 38.084008 | 38.456201 | 38.782849 | 39.314992 | 39.525630 | 39.892901 | 40.129175 |
| 50.00000 | 0.648313 | 0.851307 | 1.083924 | 1.534209 | 1.821787 | 2.136476 | 2.486289 | 3.030986 | 3.274986 | 3.247806 | ... | 38.882005 | 39.126887 | 39.298713 | 40.048118 | 40.523917 | 40.267877 | 40.865600 | 41.026446 | 41.555928 | 41.868587 | |||
| 83.55321 | 0.903945 | 1.062872 | 1.308766 | 1.852209 | 2.113444 | 2.235705 | 2.575573 | 3.282426 | 3.551149 | 3.389598 | ... | 40.722350 | 40.703267 | 41.050570 | 41.512968 | 41.849722 | 42.458905 | 42.446930 | 43.313530 | 43.181393 | 43.563652 | |||
| variable_2 | dtCO2 | 16.85321 | 0.362925 | 0.516618 | 1.042382 | 1.167780 | 1.763431 | 2.192531 | 2.373635 | 2.759656 | 3.395561 | 3.669692 | ... | 42.798163 | 43.387106 | 43.531784 | 43.937189 | 44.077462 | 44.681245 | 45.422692 | 44.999743 | 45.864256 | 45.699842 | |
| 50.00000 | 0.572768 | 0.909686 | 1.424348 | 1.539451 | 2.001265 | 2.421750 | 2.825520 | 3.323927 | 3.485649 | 3.847812 | ... | 44.500854 | 44.821495 | 45.135516 | 45.663902 | 45.918448 | 46.572523 | 46.587133 | 47.137018 | 47.379157 | 47.923352 | |||
| 83.55321 | 0.849458 | 1.205051 | 1.618410 | 1.912443 | 2.301829 | 2.865753 | 3.144360 | 3.536021 | 3.770411 | 4.104320 | ... | 46.159274 | 46.238532 | 46.898009 | 47.314295 | 47.510171 | 47.992426 | 48.678008 | 48.906217 | 49.555743 | 49.537821 |
18 rows × 125 columns
# You can control how the legend is created by passing
# in your own function
# (this allows you to also disable legend creation
# by passing in a no-op).
def create_legend(ax, handles) -> None:
"""Create custom legend"""
ax.legend(handles=handles, loc="best", handlelength=4)
_, axes = plt.subplots(ncols=2)
plot_df.openscm.plot_plume(
# Specify the axes to plot on
ax=axes[1],
quantiles_plumes=((50.0, 1.0), ((16.85321, 83.55321), 0.3)),
# Specify that the quantiles are in a column that is not the default
quantile_var="percentile",
# Custom label for the quantiles in the legend
quantile_var_label="Percent",
# Rounding when displaying the quantiles in the legend
quantile_legend_round=3,
# Variable that defines colour groupings
hue_var="variable",
# Heading to use for the colour groupings in the legend
hue_var_label="Var",
# Palette to use.
palette={
# Drop out to trigger warning below
# "variable_1": "tab:green",
"variable_2": "tab:cyan",
},
# Notice that we don't supply all values,
# which is why we get a warning.
# This can be disabled with the line below.
# warn_on_palette_value_missing=False,
# Variable that defines style groupings
style_var="scenario",
# Heading to use for the style groupings in the legend
style_var_label="Scen",
# Dashes to use for different values of the style variable.
dashes={
"scenario_0": "--",
# Drop out to trigger warning below
# "scenario_1": "-",
"scenario_2": (0, (5, 3, 5, 1)),
},
# Notice that we don't supply all values,
# which is why we get a warning.
# This can be disabled with the line below.
# warn_on_dashes_value_missing=False,
# Linewidth to use for lines
linewidth=1.5,
# The variable (column in the data's multi-index)
# that holds unit information.
unit_var="units",
# In the output, notice that a warning is raised,
# which says that the y-label is not automatically set
# because there is more than unit in the data.
# This warning can be silenced with the line below.
# (More on unit support below).
# warn_infer_y_label_with_multi_unit=False,
# Inject our own legend creation
create_legend=create_legend,
)
/home/docs/.asdf/installs/python/3.11.14/lib/python3.11/site-packages/pandas_openscm/plotting.py:556: UserWarning: Some hue values are not in the user-supplied palette, they will be filled from the default colour cycler instead. missing_from_user_supplied=['variable_1'] palette_user_supplied={'variable_2': 'tab:cyan'}
warnings.warn(msg)
/home/docs/.asdf/installs/python/3.11.14/lib/python3.11/site-packages/pandas_openscm/plotting.py:640: UserWarning: Some style values are not in the user-supplied dashes, they will be filled from the default dash cycler instead. missing_from_user_supplied=['scenario_1'] dashes_user_supplied={'scenario_0': '--', 'scenario_2': (0, (5, 3, 5, 1))}
warnings.warn(msg)
/home/docs/.asdf/installs/python/3.11.14/lib/python3.11/site-packages/pandas_openscm/plotting.py:1132: UserWarning: Not auto-setting the y_label because the plotted data has more than one unit: data units {'dtCO2', 'tCO2'}
warnings.warn(
<Axes: xlabel='time'>
Unit-aware plotting¶
Matplotlib and pint support plotting with units (stable docs, last version that we checked at the time of writing).
Firstly, set-up matplotlib to use the unit registry.
openscm_units.unit_registry.setup_matplotlib(enable=True)
Now we can tell our plottting functions to do unit-aware plotting. If we plot now, the units will appear on the output axis automatically and data will be plotted in the same units.
df_compatible_units = create_test_df(
# Notice the different, but compatible, units here
variables=(("Emissions (ct)", "ctCO2"), ("Emissions (dt)", "dtCO2")),
n_scenarios=5,
n_runs=10,
timepoints=np.arange(1850.0, 2024.0),
)
df_compatible_units.openscm.plot_plume_after_calculating_quantiles(
style_var="variable",
quantile_over="run",
quantiles_plumes=(
(0.5, 0.8),
((0.1, 0.9), 0.3),
),
# Opt into unit-aware plotting
unit_aware=openscm_units.unit_registry,
# Also specify the time axis
time_units="yr",
)
<Axes: xlabel='time', ylabel='centitCO2'>
Without unit-aware plotting, we don't see the true difference in the data.
df_compatible_units.openscm.plot_plume_after_calculating_quantiles(
style_var="variable",
quantile_over="run",
quantiles_plumes=(
(0.5, 0.8),
((0.1, 0.9), 0.3),
),
# # Opt into unit-aware plotting
# unit_aware=openscm_units.unit_registry,
# # Also specify the time axis
# time_units="yr",
# We know we have different units,
# we don't need the warning here
warn_infer_y_label_with_multi_unit=False,
)
<Axes: xlabel='time'>
Unit-aware plotting is extremely helpful for avoiding plotting incompatible units.
ax = df_compatible_units.openscm.plot_plume_after_calculating_quantiles(
style_var="variable",
quantile_over="run",
quantiles_plumes=((0.5, 0.8),),
unit_aware=openscm_units.unit_registry,
time_units="yr",
)
# If we now try and plot data with other units on these axes,
# an error is raised.
try:
df_multi_facet.openscm.plot_plume_after_calculating_quantiles(
ax=ax,
style_var="variable",
quantile_over="run",
quantiles_plumes=(((0.1, 0.9), 0.3),),
unit_aware=openscm_units.unit_registry,
time_units="yr",
)
except matplotlib.units.ConversionError:
traceback.print_exc(limit=0)
# Note that this data doesn't appear on the output plot
pint.errors.DimensionalityError: Cannot convert from 'kelvin' ([temperature]) to 'centitCO2' ([mass] * [carbon]) The above exception was the direct cause of the following exception: matplotlib.units.ConversionError: Failed to convert value(s) to axis units: <Quantity([ 0.21020956 0.16908076 0.53697665 0.5746354 0.53082686 0.86602907 0.6949364 0.79532581 0.84083112 0.98273623 1.07525733 1.12703456 1.3038521 1.30856613 1.38664726 1.4605713 1.66474323 1.99547655 2.04854369 2.0023048 2.05205344 2.0370615 2.12965198 2.52995254 2.34744386 2.62974429 2.55836688 2.94192466 2.9270134 2.9151093 3.06376572 3.07976348 3.29836292 3.39022946 3.55000338 3.39498477 3.56293553 3.58812432 3.5419877 3.73083492 3.78195705 3.88531666 4.03800314 4.12868343 4.6615062 4.51438345 4.37229665 4.50310372 4.54092808 4.87529638 4.90670489 5.06025741 4.87358811 5.26088864 5.04026121 5.21396584 5.46641926 5.57133971 5.64454761 5.79327898 6.04192111 5.90667666 5.73134732 6.08188191 6.21512747 6.26285177 6.49506027 6.28749004 6.40641148 6.5301008 6.31743259 6.69990898 6.72444288 7.0840539 7.11004048 7.19290564 7.03398964 7.12054986 7.31712774 7.68799646 7.9673819 7.65907832 7.7655559 7.81903976 7.81663065 7.70382621 8.31299086 8.09172613 8.14311956 8.12585486 8.77056744 8.52002654 8.35778345 8.5765622 8.74197459 8.98029135 8.94948839 9.23594831 9.34245226 9.10296061 9.21393079 9.21594783 9.53985218 9.39909705 9.74225154 10.00341226 9.49177005 9.83137149 9.72385823 10.32560677 10.3152076 10.58041935 10.50405502 10.84029744 11.05852952 10.4798162 10.99580571 10.4917889 10.82267317 10.78283263 10.85460532 11.29283878 11.32829093 11.39466519 11.45770843 11.52630149 11.99320901 11.70380463 11.79428487 11.89956715 11.82285138 12.26512137 11.87046758 12.53357779 12.43921604 12.26336498 12.23816894 12.44267205 12.63944192 12.45042422 13.05589454 13.11897066 12.84325874 13.1846147 13.36275265 13.56440546 13.73903434 13.99410971 13.64700059 13.88246519 13.65216536 14.07995698 13.99017639 13.95271828 14.05787108 14.38335537 14.30812994 14.23589201 14.52543814 14.54875564 14.57485037 14.83058821 15.22377319 14.93842479 15.3206719 15.42306855 15.07377121 15.2871774 15.11242403 15.51611396 15.19915198 15.65853767 15.44481791 15.96338929], 'kelvin')>
PlumePlotter¶
Underneath these plots is the PlumePlotter class. If you want really fine-grained control of your plots, we recommend dropping down to using this class directly.
# Initialise our plume_plotter
plume_plotter = PlumePlotter.from_df(
df_multi_facet.openscm.groupby_except("run")
.quantile([0.1, 0.5, 0.9])
.openscm.fix_index_name_after_groupby_quantile(),
style_var="variable",
quantiles_plumes=(
(0.5, 0.8),
((0.1, 0.9), 0.3),
),
unit_aware=openscm_units.unit_registry,
time_units="yr",
# Don't infer any labels for the axes
x_label=None,
y_label=None,
)
print(f"{len(plume_plotter.lines)=}")
print(f"{len(plume_plotter.plumes)=}")
len(plume_plotter.lines)=10 len(plume_plotter.plumes)=10
df_multi_facet
| 1850.0 | 1851.0 | 1852.0 | 1853.0 | 1854.0 | 1855.0 | 1856.0 | 1857.0 | 1858.0 | 1859.0 | ... | 2014.0 | 2015.0 | 2016.0 | 2017.0 | 2018.0 | 2019.0 | 2020.0 | 2021.0 | 2022.0 | 2023.0 | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| scenario | variable | run | unit | |||||||||||||||||||||
| scenario_0 | Warming land | 0 | K | 0.298867 | 0.742854 | 0.563424 | 1.250258 | 1.327463 | 1.214002 | 0.705480 | 1.129024 | 1.279976 | 1.235948 | ... | 14.976024 | 14.775867 | 15.076344 | 15.296768 | 14.936247 | 15.552726 | 14.744918 | 15.587228 | 15.308825 | 15.910943 |
| 1 | K | 0.221348 | 0.450243 | 0.714069 | 0.274619 | 0.955114 | 1.215479 | 1.303514 | 1.381276 | 0.771265 | 1.358581 | ... | 15.442051 | 15.584161 | 15.050620 | 15.200864 | 15.131999 | 15.186610 | 15.249622 | 15.666461 | 15.459928 | 15.969217 | ||
| 2 | K | 0.498379 | 0.473688 | 1.158913 | 0.607971 | 0.804691 | 0.897335 | 0.600045 | 0.737892 | 1.319670 | 1.748600 | ... | 15.358966 | 15.848383 | 15.359692 | 15.508057 | 15.995742 | 15.989497 | 15.997951 | 16.023103 | 15.857921 | 16.693652 | ||
| 3 | K | 0.379958 | 0.502763 | 0.995504 | 0.803619 | 0.443273 | 1.218133 | 0.855000 | 1.269989 | 0.926568 | 1.000908 | ... | 15.929081 | 15.494980 | 16.280599 | 16.383576 | 16.368982 | 16.023928 | 16.679865 | 16.353694 | 16.604486 | 16.909772 | ||
| 4 | K | 0.109965 | 0.932942 | 1.010373 | 1.065242 | 0.540555 | 0.684711 | 0.753612 | 1.463553 | 1.046784 | 1.097672 | ... | 15.596846 | 16.640762 | 16.553569 | 16.654136 | 16.013552 | 16.914100 | 16.739866 | 16.363916 | 16.608080 | 17.054289 | ||
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| scenario_4 | Warming ocean | 5 | K | 0.304057 | 0.901887 | 0.886101 | 1.382668 | 1.255751 | 2.037893 | 1.785548 | 2.830568 | 2.401498 | 2.738431 | ... | 46.940607 | 46.945518 | 47.533063 | 47.125046 | 48.067805 | 47.502240 | 48.114571 | 48.042380 | 49.182756 | 49.494835 |
| 6 | K | 0.777510 | 1.097260 | 0.646037 | 1.225935 | 1.359844 | 1.753665 | 2.330423 | 2.124079 | 2.501717 | 2.801696 | ... | 46.687102 | 47.591805 | 47.392034 | 47.377299 | 48.357250 | 48.621725 | 48.862765 | 48.424290 | 48.824302 | 49.402734 | ||
| 7 | K | 0.410711 | 0.827368 | 1.063549 | 1.169650 | 1.963039 | 1.644926 | 1.893075 | 2.270161 | 2.686984 | 3.078402 | ... | 47.694651 | 47.759772 | 47.946279 | 48.111884 | 48.196201 | 49.015201 | 48.726953 | 48.764211 | 49.491920 | 49.703605 | ||
| 8 | K | 0.444218 | 0.461465 | 1.264547 | 1.463307 | 1.581928 | 1.891313 | 2.441039 | 2.638465 | 2.962636 | 2.868656 | ... | 47.605104 | 47.419685 | 48.565996 | 48.149693 | 48.475385 | 49.066547 | 49.328749 | 49.969833 | 49.988480 | 50.057656 | ||
| 9 | K | 0.523415 | 0.641830 | 0.861649 | 1.179819 | 1.537636 | 2.193138 | 2.064470 | 2.024533 | 2.366872 | 3.250463 | ... | 47.662932 | 48.406743 | 48.361738 | 48.366975 | 48.982146 | 49.236314 | 49.961707 | 49.953742 | 50.169330 | 50.750052 |
100 rows × 174 columns
# Now make our plots
fig, ax = plt.subplots()
ax.xaxis.set_units(openscm_units.unit_registry("yr").u)
ax.yaxis.set_units(openscm_units.unit_registry("K").u)
# Some other data we want to plot
Q = openscm_units.unit_registry.Quantity
history_lines = ax.plot(
Q(np.arange(0, 30 * 12.0) + 1998.0 * 12, "month"),
Q(
10_000 * np.sin(np.arange(0, 30 * 12.0) / 20.0) + 100 * np.arange(0, 30 * 12.0),
"mK",
),
label="history",
color="black",
zorder=3,
linewidth=3,
)
scatter_points = ax.scatter(
Q([1910, 1930, 1980], "yr"),
Q([8, 3, 15], "K"),
label="interesting points",
color="tab:pink",
zorder=3,
linewidth=3,
marker="x",
s=120,
)
# Plot using the plume plotter, without making a legend
plume_plotter.plot(ax=ax, create_legend=lambda x, y: ...)
# Get the legend items from the plume plotter
plume_legend_items = plume_plotter.generate_legend_handles(quantile_legend_round=3)
# Plot something else
other_history_lines = ax.plot(
Q(np.arange(0, 30 * 12.0) + 1998.0 * 12, "month"),
Q(
10_000 * (1 - np.sin(np.arange(0, 30 * 12.0) / 20.0))
+ 100 * np.arange(0, 30 * 12.0),
"mK",
),
label="history data 2",
color="gray",
zorder=3,
linewidth=3,
)
# Now make a legend
ax.legend(
handles=[
*history_lines,
*other_history_lines,
scatter_points,
*plume_legend_items,
],
loc="center left",
bbox_to_anchor=(1.05, 0.5),
)
# Can also modify all other parts of the plot (as normal)
ax.set_xlabel("Year")
ax.set_ylabel("K")
ax.set_title("Demo")
Text(0.5, 1.0, 'Demo')
Summary¶
We use the plume plotting a lot Like the rest of the library, it is in a work progress. If there is a feature you would like or a bug or anything else, please raise an issue.