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.010618 | 1.429803 | 2.633243 | 3.838340 | 4.450179 | 5.895702 | 6.447885 | 7.808579 | 9.293535 | 10.187458 | 10.733374 | 12.634868 | 13.424782 | 14.643785 | 15.727634 |
| 1 | K | 0.687626 | 1.627281 | 3.055054 | 3.489100 | 4.539586 | 5.973592 | 7.487817 | 8.490015 | 9.681646 | 10.220227 | 12.060904 | 12.678563 | 14.085315 | 15.441439 | 15.849305 | ||
| 2 | K | 0.651886 | 1.306915 | 2.425539 | 4.408856 | 4.781538 | 6.566607 | 7.069275 | 8.337274 | 9.907674 | 11.297246 | 12.675095 | 13.299998 | 14.307123 | 15.773371 | 16.630671 | ||
| 3 | K | 0.013181 | 1.261819 | 2.801122 | 4.149001 | 5.375430 | 6.755657 | 8.026065 | 8.798250 | 10.490862 | 11.935011 | 12.533179 | 13.798169 | 15.233265 | 16.411481 | 17.598419 | ||
| 4 | K | 0.978772 | 1.657309 | 3.068873 | 4.206163 | 5.144449 | 6.758808 | 8.083164 | 9.285526 | 10.374809 | 11.900384 | 13.031835 | 14.045486 | 15.402207 | 16.720450 | 17.977306 | ||
| 5 | K | 0.640035 | 1.519441 | 3.493699 | 4.262643 | 5.576105 | 6.956352 | 8.335653 | 9.410668 | 11.151737 | 12.387873 | 13.328491 | 14.759059 | 16.787668 | 17.468271 | 19.217518 | ||
| 6 | K | 0.190702 | 2.149196 | 3.246190 | 4.712136 | 5.756309 | 7.769303 | 8.481436 | 10.133333 | 11.888544 | 13.310262 | 13.865600 | 15.183055 | 16.610081 | 17.992114 | 20.037772 | ||
| 7 | K | 0.270043 | 2.073600 | 3.498242 | 4.739087 | 5.861423 | 7.659246 | 9.456062 | 10.624512 | 12.000500 | 13.403901 | 14.842390 | 16.238843 | 17.689992 | 19.199054 | 20.953526 | ||
| 8 | K | 0.828091 | 1.981996 | 3.772528 | 4.657146 | 5.930720 | 7.546649 | 9.577800 | 11.265829 | 11.870513 | 13.745528 | 14.952762 | 16.349339 | 18.216873 | 19.625526 | 20.879383 | ||
| 9 | K | 0.363956 | 1.573496 | 4.018873 | 5.403070 | 6.275113 | 7.745876 | 9.496840 | 11.661207 | 12.552094 | 14.556240 | 15.557006 | 16.989159 | 19.246982 | 20.696067 | 22.129484 | ||
| scenario_1 | Warming | 0 | K | 0.427405 | 2.503617 | 3.497004 | 5.356994 | 7.041979 | 8.049941 | 10.035760 | 11.740587 | 12.901189 | 14.243733 | 15.951484 | 18.212920 | 19.880661 | 21.250418 | 22.343403 |
| 1 | K | 0.633503 | 2.159498 | 3.923371 | 5.784413 | 7.090117 | 8.737798 | 10.533340 | 11.494408 | 13.354767 | 15.146284 | 17.280961 | 18.640477 | 20.510043 | 21.548530 | 23.209510 | ||
| 2 | K | 0.546739 | 1.936725 | 3.552370 | 5.912780 | 7.700524 | 8.767918 | 10.859492 | 12.783282 | 13.647755 | 15.821377 | 17.533478 | 18.901060 | 20.547484 | 22.866611 | 23.708766 | ||
| 3 | K | 0.192118 | 2.085755 | 3.733598 | 5.878481 | 7.594772 | 8.941996 | 10.810914 | 12.542134 | 14.671953 | 16.229441 | 17.753023 | 19.514328 | 21.188575 | 22.818038 | 25.272858 | ||
| 4 | K | 0.846769 | 2.516847 | 3.909206 | 5.441032 | 7.238820 | 9.015125 | 11.431550 | 13.252204 | 14.987538 | 16.930950 | 18.221059 | 20.101420 | 21.783585 | 24.211554 | 25.413589 | ||
| 5 | K | 0.805659 | 2.171738 | 3.758267 | 6.398411 | 8.275906 | 9.242831 | 11.175990 | 13.754096 | 15.656767 | 17.317097 | 18.710015 | 20.933706 | 22.189825 | 24.807496 | 25.923725 | ||
| 6 | K | 0.590457 | 2.035074 | 4.733571 | 6.028506 | 8.146828 | 9.570793 | 12.203632 | 13.693556 | 15.452825 | 17.095784 | 19.631303 | 21.723603 | 22.794203 | 25.182443 | 26.536118 | ||
| 7 | K | 0.900537 | 2.406953 | 4.623571 | 6.619461 | 8.399629 | 9.849221 | 12.341089 | 13.856861 | 15.863430 | 18.442203 | 20.167801 | 22.072779 | 24.058323 | 25.654403 | 28.042281 | ||
| 8 | K | 0.428121 | 2.433513 | 4.321215 | 6.519129 | 8.157058 | 10.235754 | 12.750340 | 13.929215 | 15.927991 | 18.232026 | 20.276338 | 22.845972 | 24.194552 | 26.045166 | 28.608141 | ||
| 9 | K | 0.291203 | 2.700603 | 4.291324 | 6.173849 | 8.199336 | 11.128737 | 12.265908 | 14.975108 | 16.941490 | 19.028627 | 20.509572 | 23.035566 | 25.122072 | 26.674860 | 28.782760 | ||
| scenario_2 | Warming | 0 | K | 0.596416 | 2.129611 | 5.136523 | 6.463460 | 8.462424 | 10.641864 | 13.216805 | 15.324837 | 17.086581 | 19.226157 | 21.905101 | 23.365698 | 26.059738 | 27.327469 | 29.555689 |
| 1 | K | 0.520641 | 2.396235 | 5.117642 | 7.382647 | 9.260193 | 10.864901 | 13.046735 | 15.432641 | 17.761674 | 19.885219 | 21.952561 | 24.074053 | 26.529658 | 28.035298 | 30.258465 | ||
| 2 | K | 0.858066 | 2.328675 | 4.590896 | 6.722384 | 9.579286 | 11.500788 | 13.406228 | 15.422320 | 18.372376 | 20.184364 | 22.680624 | 24.371929 | 27.059236 | 29.006953 | 31.266777 | ||
| 3 | K | 0.957815 | 2.249755 | 5.326814 | 7.659312 | 9.915369 | 11.273701 | 14.007820 | 16.522759 | 18.917744 | 21.008760 | 22.690015 | 24.723189 | 27.379889 | 30.176196 | 31.506716 | ||
| 4 | K | 0.423105 | 2.774557 | 5.435316 | 7.819972 | 9.333485 | 12.359471 | 14.735566 | 17.049462 | 19.252780 | 20.868914 | 23.856030 | 25.310801 | 27.675544 | 30.750699 | 33.042432 | ||
| 5 | K | 0.773859 | 3.246346 | 5.157913 | 7.534629 | 9.760365 | 12.722872 | 15.073355 | 17.124500 | 19.706715 | 21.202102 | 23.959514 | 26.644117 | 28.715657 | 30.957442 | 32.988135 | ||
| 6 | K | 0.827330 | 3.388205 | 5.037109 | 7.732589 | 10.410798 | 12.807307 | 14.803438 | 17.598360 | 19.955927 | 21.974891 | 24.602290 | 26.886640 | 29.606261 | 31.271982 | 34.123392 | ||
| 7 | K | 0.950732 | 3.029302 | 5.297972 | 8.111897 | 9.823831 | 12.595356 | 15.162723 | 17.902272 | 20.457329 | 22.696806 | 24.680240 | 27.787489 | 30.174292 | 32.154894 | 35.190857 | ||
| 8 | K | 0.888925 | 2.913268 | 5.920140 | 7.932721 | 10.286962 | 13.089062 | 15.466515 | 18.329065 | 20.018907 | 22.631008 | 25.828585 | 27.769153 | 30.417990 | 32.562043 | 35.911937 | ||
| 9 | K | 0.944868 | 2.585726 | 5.739839 | 8.342736 | 11.185149 | 13.673432 | 16.118569 | 18.330032 | 20.606540 | 23.177249 | 26.116110 | 28.489477 | 31.178116 | 33.803810 | 35.796239 | ||
| scenario_3 | Warming | 0 | K | 0.399025 | 3.410800 | 5.793979 | 8.191400 | 10.881003 | 13.085474 | 15.978001 | 18.433439 | 21.017813 | 24.069818 | 26.117428 | 29.158777 | 32.134817 | 34.220672 | 37.204668 |
| 1 | K | 0.624570 | 3.223005 | 5.698723 | 8.633048 | 10.715234 | 13.900387 | 16.237188 | 18.658389 | 21.573521 | 24.201165 | 26.934031 | 30.079387 | 32.341801 | 35.282410 | 37.852466 | ||
| 2 | K | 0.202759 | 3.020142 | 5.544011 | 8.639713 | 11.627542 | 13.608263 | 16.694621 | 19.232824 | 22.031170 | 25.146037 | 27.382155 | 30.594230 | 32.613698 | 35.980812 | 38.217012 | ||
| 3 | K | 0.876447 | 2.768772 | 6.237092 | 8.596088 | 11.035882 | 13.865766 | 16.714421 | 19.908808 | 22.207969 | 25.021509 | 27.573396 | 30.814743 | 33.272888 | 35.953548 | 39.521064 | ||
| 4 | K | 0.249307 | 3.759265 | 5.809466 | 9.125346 | 11.470400 | 14.285016 | 16.857589 | 20.228459 | 22.795843 | 25.504685 | 28.596925 | 31.824080 | 33.959627 | 37.468393 | 40.019148 | ||
| 5 | K | 0.751233 | 3.646045 | 6.333345 | 8.666237 | 11.572866 | 14.880259 | 17.877598 | 20.634016 | 22.966178 | 25.884622 | 28.915650 | 31.691472 | 34.660855 | 38.037467 | 40.753601 | ||
| 6 | K | 0.284979 | 3.336386 | 6.680626 | 9.065700 | 11.999526 | 15.445773 | 17.745614 | 20.905227 | 23.930312 | 26.572940 | 29.463466 | 32.513041 | 35.137908 | 38.230867 | 41.341434 | ||
| 7 | K | 0.504261 | 3.203270 | 6.228673 | 9.189995 | 12.105822 | 14.859773 | 18.243441 | 21.529195 | 24.010573 | 27.074828 | 30.073428 | 33.546587 | 36.113514 | 39.304833 | 41.597735 | ||
| 8 | K | 0.575020 | 3.662988 | 6.940262 | 9.399899 | 12.284724 | 15.919220 | 18.164240 | 21.969509 | 25.004786 | 27.353357 | 30.872379 | 33.713796 | 36.541187 | 39.188251 | 42.424790 | ||
| 9 | K | 0.738489 | 3.857594 | 6.733096 | 9.983896 | 12.485512 | 16.283406 | 19.021141 | 22.398948 | 24.539861 | 27.761804 | 30.708802 | 34.595349 | 37.266194 | 40.595097 | 43.316344 | ||
| scenario_4 | Warming | 0 | K | 0.531064 | 3.916357 | 7.196446 | 9.459578 | 13.205886 | 15.931324 | 18.760448 | 21.847201 | 25.600519 | 28.588897 | 31.883832 | 34.390739 | 37.401070 | 40.745761 | 44.402040 |
| 1 | K | 0.069547 | 4.122992 | 7.068370 | 9.762156 | 13.401388 | 16.589489 | 19.482264 | 22.360052 | 25.680375 | 28.539645 | 31.863525 | 35.257532 | 38.141322 | 41.414068 | 44.999951 | ||
| 2 | K | 0.448104 | 3.668042 | 7.049161 | 10.607217 | 12.893524 | 17.007755 | 20.014106 | 23.010893 | 25.772576 | 29.549134 | 32.822377 | 35.497124 | 38.926834 | 42.430920 | 45.795652 | ||
| 3 | K | 0.060995 | 3.928131 | 7.279427 | 10.087069 | 13.362112 | 16.824254 | 20.147146 | 23.032304 | 26.793080 | 29.553791 | 33.195234 | 36.780614 | 39.871259 | 42.566834 | 45.907074 | ||
| 4 | K | 0.205997 | 3.931573 | 7.310165 | 10.416462 | 13.377917 | 17.437175 | 20.334054 | 23.962966 | 26.704798 | 30.639344 | 33.251293 | 36.881823 | 40.084959 | 43.449792 | 47.096108 | ||
| 5 | K | 0.625058 | 3.738139 | 6.919824 | 10.111885 | 14.342446 | 17.049780 | 20.616891 | 24.385069 | 27.663583 | 31.283217 | 34.469952 | 37.273988 | 41.202294 | 44.092369 | 47.860338 | ||
| 6 | K | 0.088006 | 3.591810 | 6.939914 | 10.271090 | 14.531533 | 17.102283 | 20.916221 | 24.462820 | 27.700594 | 31.016201 | 34.306222 | 38.031430 | 41.557847 | 45.039232 | 48.031851 | ||
| 7 | K | 0.443615 | 3.488299 | 7.677009 | 11.386071 | 14.020060 | 17.465500 | 21.621778 | 24.821316 | 28.511077 | 31.765949 | 35.388351 | 39.019482 | 42.203721 | 45.635180 | 48.625347 | ||
| 8 | K | 0.986800 | 4.197992 | 7.658301 | 11.046911 | 14.465721 | 17.627358 | 21.936825 | 24.778263 | 28.530998 | 32.233687 | 35.342271 | 39.616371 | 42.658648 | 46.161823 | 49.804523 | ||
| 9 | K | 0.637656 | 3.662212 | 7.484664 | 10.773196 | 14.315735 | 18.255468 | 22.388835 | 25.826567 | 28.743651 | 32.657346 | 35.840245 | 39.820332 | 43.350138 | 46.657560 | 50.712772 |
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.011772 | 1.282112 | 2.519006 | 3.646258 | 4.490412 | 5.930753 | 6.727510 | 8.046492 | 9.468185 | 10.202204 | 11.330762 | 12.654531 | 13.722022 | 15.002729 | 15.782386 |
| 0.25 | 0.210537 | 1.452213 | 2.864605 | 4.163291 | 4.872266 | 6.613870 | 7.622379 | 8.567074 | 10.024457 | 11.448030 | 12.568658 | 13.424541 | 14.538658 | 15.932898 | 16.872608 | |||
| 0.50 | 0.501995 | 1.600388 | 3.157532 | 4.335750 | 5.475767 | 6.857580 | 8.209409 | 9.348097 | 10.821299 | 12.161442 | 13.180163 | 14.402273 | 16.006144 | 17.094360 | 18.597412 | |||
| 0.75 | 0.678691 | 1.900824 | 3.497106 | 4.698389 | 5.835144 | 7.631097 | 9.212406 | 10.501717 | 11.884036 | 13.380491 | 14.598193 | 15.974896 | 17.464411 | 18.897319 | 20.668980 | |||
| 0.95 | 0.910965 | 2.115178 | 3.908018 | 5.104278 | 6.120136 | 7.758761 | 9.541368 | 11.483287 | 12.303877 | 14.191419 | 15.285096 | 16.701240 | 18.783433 | 20.214323 | 21.600303 | |||
| scenario_1 | Warming | K | 0.05 | 0.236707 | 1.980982 | 3.521918 | 5.394811 | 7.063641 | 8.359477 | 10.259671 | 11.605189 | 13.105299 | 14.649881 | 16.549749 | 18.405321 | 20.163883 | 21.384568 | 22.733151 |
| 0.25 | 0.427584 | 2.104191 | 3.739765 | 5.807930 | 7.327808 | 8.811437 | 10.823058 | 12.602421 | 13.903804 | 15.923393 | 17.588364 | 19.054377 | 20.707757 | 22.830181 | 24.099789 | |||
| 0.50 | 0.568598 | 2.289346 | 3.916288 | 5.970643 | 7.923676 | 9.128978 | 11.303770 | 13.472880 | 15.220182 | 17.013367 | 18.465537 | 20.517563 | 21.986705 | 24.509525 | 25.668657 | |||
| 0.75 | 0.762620 | 2.486091 | 4.313742 | 6.342271 | 8.188766 | 9.779614 | 12.250339 | 13.831170 | 15.811764 | 18.003294 | 20.033677 | 21.985485 | 23.742293 | 25.536413 | 27.665741 | |||
| 0.95 | 0.876341 | 2.617913 | 4.684071 | 6.574312 | 8.343954 | 10.726895 | 12.566177 | 14.504456 | 16.485415 | 18.764736 | 20.404617 | 22.950248 | 24.704688 | 26.391498 | 28.704181 | |||
| scenario_2 | Warming | K | 0.05 | 0.466996 | 2.183676 | 4.791692 | 6.579976 | 8.821420 | 10.742230 | 13.123266 | 15.368704 | 17.390373 | 19.522735 | 21.926458 | 23.684458 | 26.271202 | 27.645992 | 29.871938 |
| 0.25 | 0.640777 | 2.345565 | 5.122362 | 7.420643 | 9.394935 | 11.330473 | 13.556626 | 15.705170 | 18.508718 | 20.355502 | 22.682972 | 24.459744 | 27.139399 | 29.299263 | 31.326762 | |||
| 0.50 | 0.842698 | 2.680142 | 5.227943 | 7.695951 | 9.792098 | 12.477413 | 14.769502 | 17.086981 | 19.479748 | 21.105431 | 23.907772 | 25.977459 | 28.195600 | 30.854071 | 33.015284 | |||
| 0.75 | 0.930882 | 3.000294 | 5.408191 | 7.904534 | 10.194063 | 12.786199 | 15.140381 | 17.826294 | 20.003162 | 22.466979 | 24.660753 | 27.548525 | 30.032284 | 31.934166 | 34.923991 | |||
| 0.95 | 0.954628 | 3.324369 | 5.839005 | 8.238859 | 10.836691 | 13.410465 | 15.825145 | 18.329597 | 20.539395 | 22.961049 | 25.986723 | 28.173582 | 30.836059 | 33.245015 | 35.859873 | |||
| scenario_3 | Warming | K | 0.05 | 0.223706 | 2.881888 | 5.613631 | 8.373510 | 10.789830 | 13.320729 | 16.094635 | 18.534666 | 21.267882 | 24.128925 | 26.484899 | 29.573052 | 32.227960 | 34.698454 | 37.496177 |
| 0.25 | 0.313490 | 3.208204 | 5.797851 | 8.634715 | 11.144512 | 13.874421 | 16.699571 | 19.401820 | 22.075369 | 25.052641 | 27.429965 | 30.649358 | 32.778496 | 35.960364 | 38.543025 | |||
| 0.50 | 0.539640 | 3.373593 | 6.232882 | 8.865968 | 11.600204 | 14.572395 | 17.301602 | 20.431237 | 22.881010 | 25.694654 | 28.756287 | 31.757776 | 34.310241 | 37.752930 | 40.386374 | |||
| 0.75 | 0.710009 | 3.658752 | 6.593806 | 9.173833 | 12.079248 | 15.304394 | 18.092579 | 21.373203 | 23.990508 | 26.949356 | 29.920938 | 33.288201 | 35.869612 | 38.948905 | 41.533660 | |||
| 0.95 | 0.820101 | 3.813346 | 6.847038 | 9.721097 | 12.395158 | 16.119522 | 18.671176 | 22.205700 | 24.795570 | 27.578003 | 30.798769 | 34.198650 | 36.939941 | 40.014478 | 42.915145 | |||
| scenario_4 | Warming | K | 0.05 | 0.064843 | 3.534879 | 6.928865 | 9.595738 | 13.034087 | 16.227498 | 19.085265 | 22.077984 | 25.636455 | 28.561808 | 31.872663 | 34.780796 | 37.734183 | 41.046499 | 44.671100 |
| 0.25 | 0.117503 | 3.663670 | 7.053963 | 10.093273 | 13.366063 | 16.870129 | 20.047366 | 23.016245 | 26.005631 | 29.550298 | 32.915591 | 35.817996 | 39.162940 | 42.464899 | 45.823507 | |||
| 0.50 | 0.445859 | 3.827248 | 7.237937 | 10.343776 | 13.710724 | 17.076032 | 20.475472 | 24.174017 | 27.228331 | 30.827772 | 33.778758 | 37.077905 | 40.643626 | 43.771081 | 47.478223 | |||
| 0.75 | 0.601559 | 3.930713 | 7.441039 | 10.731702 | 14.335768 | 17.458418 | 21.445389 | 24.699402 | 28.308456 | 31.645266 | 35.124191 | 38.772469 | 42.042253 | 45.486193 | 48.476973 | |||
| 0.95 | 0.829685 | 4.164242 | 7.668590 | 11.233449 | 14.501917 | 17.972819 | 22.185430 | 25.374204 | 28.647957 | 32.466699 | 35.636893 | 39.728549 | 43.038967 | 46.434478 | 50.304060 |
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.075652 | 0.386421 | 0.404361 | 0.577212 | 1.056823 | 0.855240 | 1.026217 | 1.233784 | 1.533624 | 1.600186 | ... | 15.272998 | 14.922327 | 15.878413 | 15.626755 | 15.752515 | 15.450281 | 15.688342 | 15.999974 | 16.162843 | 16.686123 |
| 50.00000 | 0.610410 | 0.743561 | 0.723046 | 0.855729 | 1.263604 | 1.058696 | 1.275059 | 1.521408 | 1.873764 | 1.940044 | ... | 16.887716 | 17.215830 | 17.071703 | 17.362644 | 17.552760 | 17.623175 | 17.859282 | 17.818623 | 17.792221 | 18.517863 | |||
| 83.55321 | 0.744432 | 0.916247 | 0.950985 | 1.388682 | 1.431875 | 1.251077 | 1.505503 | 1.645853 | 1.978386 | 2.084916 | ... | 18.806545 | 18.611939 | 18.909962 | 18.930188 | 19.030132 | 19.371804 | 19.470305 | 19.792478 | 19.648903 | 19.818992 | |||
| variable_2 | dtCO2 | 16.85321 | 0.389083 | 0.370522 | 0.433013 | 0.961683 | 1.145855 | 1.117399 | 1.502602 | 1.539762 | 1.848570 | 1.985763 | ... | 20.723370 | 21.125398 | 21.200247 | 21.096984 | 21.457962 | 21.496614 | 21.686809 | 21.965855 | 22.309405 | 22.448676 | |
| 50.00000 | 0.671622 | 0.729784 | 0.809976 | 1.318916 | 1.397321 | 1.601120 | 1.948320 | 1.898428 | 2.116152 | 2.201174 | ... | 22.481097 | 22.739172 | 22.723259 | 22.826314 | 22.753357 | 23.568681 | 23.448707 | 23.308890 | 23.863039 | 24.021323 | |||
| 83.55321 | 0.930771 | 1.009213 | 1.094616 | 1.488078 | 1.511687 | 1.803471 | 2.032975 | 2.197672 | 2.341551 | 2.572824 | ... | 23.921435 | 24.296236 | 24.599826 | 24.504944 | 24.916215 | 25.040358 | 25.486844 | 25.492446 | 25.424877 | 26.221263 | |||
| scenario_1 | variable_1 | tCO2 | 16.85321 | 0.155440 | 0.376556 | 0.765851 | 1.024779 | 1.227235 | 1.392433 | 1.673167 | 1.847383 | 2.043010 | 2.322291 | ... | 26.217018 | 26.712947 | 26.432332 | 26.686795 | 27.019597 | 27.452586 | 27.593032 | 27.401893 | 28.299204 | 28.185866 |
| 50.00000 | 0.395691 | 0.533883 | 1.197842 | 1.278446 | 1.627668 | 1.560341 | 2.184473 | 2.137265 | 2.239866 | 2.660474 | ... | 28.055336 | 28.128096 | 28.585535 | 28.710484 | 28.706727 | 29.002145 | 29.245262 | 29.656042 | 29.716704 | 30.063463 | |||
| 83.55321 | 0.725069 | 0.901886 | 1.327912 | 1.580879 | 1.675033 | 1.881557 | 2.340002 | 2.438307 | 2.617874 | 3.021656 | ... | 29.511808 | 29.581959 | 30.105567 | 30.156992 | 30.605712 | 31.134227 | 31.122962 | 31.214033 | 31.274441 | 31.761538 | |||
| variable_2 | dtCO2 | 16.85321 | 0.196525 | 0.915507 | 0.694822 | 1.122154 | 1.307702 | 1.729667 | 1.807591 | 2.231270 | 2.690378 | 2.845708 | ... | 31.643565 | 32.093662 | 31.973662 | 32.296334 | 32.989773 | 33.002160 | 33.478840 | 33.736867 | 33.960898 | 34.424107 | |
| 50.00000 | 0.761278 | 1.002891 | 0.951749 | 1.477724 | 1.432669 | 1.886740 | 2.130674 | 2.435843 | 2.890007 | 3.085696 | ... | 33.215206 | 33.378897 | 34.009169 | 34.314231 | 34.583892 | 34.715543 | 35.047320 | 35.417628 | 35.384521 | 36.067113 | |||
| 83.55321 | 0.819794 | 1.167063 | 1.241906 | 1.674474 | 1.597533 | 2.226578 | 2.570547 | 2.712895 | 3.089115 | 3.420361 | ... | 34.908224 | 35.448252 | 35.610884 | 35.605422 | 36.082253 | 36.467527 | 37.088512 | 37.239575 | 37.027967 | 37.765676 | |||
| scenario_2 | variable_1 | tCO2 | 16.85321 | 0.164274 | 0.546882 | 1.014968 | 1.223838 | 1.391345 | 1.822373 | 2.201873 | 2.556590 | 2.847173 | 2.996814 | ... | 37.586668 | 37.536609 | 37.769530 | 38.596888 | 38.344184 | 39.133478 | 39.108390 | 39.446269 | 39.619681 | 40.188768 |
| 50.00000 | 0.406523 | 0.685695 | 1.110453 | 1.622970 | 1.512463 | 2.127931 | 2.341311 | 2.907869 | 3.258204 | 3.431640 | ... | 38.622512 | 39.213504 | 39.398253 | 40.065967 | 40.173356 | 40.398732 | 40.962068 | 41.146027 | 41.747414 | 42.320651 | |||
| 83.55321 | 0.673570 | 0.958688 | 1.466224 | 1.851902 | 1.784292 | 2.384184 | 2.620407 | 3.043548 | 3.404919 | 3.907562 | ... | 40.677619 | 41.126016 | 41.174142 | 41.501890 | 41.903941 | 42.306986 | 42.472373 | 42.793777 | 43.479729 | 44.127395 | |||
| variable_2 | dtCO2 | 16.85321 | 0.366354 | 0.691957 | 0.900564 | 1.289209 | 1.944911 | 2.087090 | 2.629813 | 2.975926 | 3.192949 | 3.613944 | ... | 42.950975 | 43.297767 | 43.259192 | 43.760675 | 44.227186 | 44.671421 | 45.118946 | 45.208950 | 45.708628 | 45.909243 | |
| 50.00000 | 0.580958 | 0.751575 | 1.103418 | 1.632500 | 2.150881 | 2.385050 | 2.895496 | 3.259537 | 3.472429 | 4.103450 | ... | 44.192332 | 45.121664 | 45.248813 | 45.894641 | 46.339579 | 46.563075 | 46.976402 | 47.234277 | 47.646428 | 47.654016 | |||
| 83.55321 | 0.721660 | 1.054841 | 1.483841 | 2.027340 | 2.445218 | 2.657186 | 3.216345 | 3.520886 | 3.641734 | 4.298644 | ... | 46.027541 | 46.541640 | 46.790723 | 47.579549 | 47.689324 | 47.956990 | 48.507959 | 48.523338 | 49.120336 | 49.823046 |
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 {'tCO2', 'dtCO2'}
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.18568602 0.22739899 0.37770688 0.32176175 0.55170172 0.83145049 0.71142943 0.92476602 0.87608992 1.04190762 1.24285174 1.16824686 1.33104115 1.32018722 1.61268556 1.5785996 1.60787122 1.87405018 2.01745079 1.94572131 1.95361189 2.0818568 2.08292068 2.15670968 2.58385667 2.50370235 2.76147772 2.62907964 3.06142734 2.93628448 2.931742 2.87094349 3.15289048 3.12195495 3.34964521 3.62071128 3.85680078 3.74970104 3.6286127 4.01793566 3.73024369 4.28310197 4.28455971 4.25821692 4.36957042 4.090233 4.60459034 4.39736238 4.63574649 4.91240463 4.90758532 5.02158634 4.83434398 4.91415259 5.23618342 5.08230718 5.5042338 5.11901341 5.338945 5.84283778 5.80711054 6.15181236 6.10153893 5.63553136 5.97550856 6.05311778 6.40358238 6.23339702 6.60342321 6.80034017 6.77152036 6.58061497 6.73261818 6.94148206 6.99216549 7.37094838 7.01980667 7.37173683 7.39531465 7.54255883 7.49966158 7.95891923 7.96979325 7.79900943 8.23609134 8.29995257 8.2501241 8.15272027 8.25351389 8.31620606 8.29605915 8.67948491 8.50900721 8.52665328 8.90230575 8.53067357 9.06502271 9.05495241 9.46187324 9.11058779 9.57096464 9.22031147 9.70343567 9.57389736 9.85796275 9.68879362 9.77636565 9.58140207 10.04018849 10.03095368 10.05571487 10.53882643 10.45516376 10.66435397 10.68239032 10.4747723 10.69268798 10.77705082 10.50909778 11.14749211 10.9742506 10.77852264 11.23500409 11.37068635 11.62894191 11.52001716 11.87105369 11.50504424 12.01471985 11.72865772 11.94884211 12.19882608 12.30145838 12.23355759 12.16724398 12.69713687 12.69039314 13.0037264 12.90357893 12.67978078 12.59825634 13.17453039 13.13940256 13.30054611 13.60370525 13.49967601 13.66689043 13.82478219 13.42469436 13.40014977 13.30505224 13.81123226 13.90573938 14.04872904 13.99233968 14.39915864 14.41655604 14.23413646 14.62598729 14.55171556 15.05860937 14.41571853 14.67158368 14.80629156 15.04250447 15.1446286 14.8921262 15.29186405 15.51215951 15.57416264 15.40492044 15.87392826 15.34273499 15.8058354 ], '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.196716 | 0.586436 | 0.458372 | 0.346803 | 0.637213 | 1.198412 | 1.355618 | 0.848514 | 1.673433 | 1.645569 | ... | 15.157557 | 15.184220 | 14.419378 | 14.753637 | 15.532432 | 14.879108 | 15.107270 | 15.029953 | 14.952859 | 15.815021 |
| 1 | K | 0.738000 | 0.313012 | 0.384646 | 1.029967 | 1.242600 | 0.851871 | 0.717495 | 0.933238 | 1.053105 | 1.332982 | ... | 15.052619 | 14.788305 | 14.944654 | 15.351667 | 15.720322 | 15.651391 | 15.760460 | 15.967703 | 15.386055 | 16.192179 | ||
| 2 | K | 0.427711 | 1.069688 | 0.456460 | 0.426925 | 0.560541 | 0.927100 | 0.774246 | 1.150598 | 0.883676 | 1.158250 | ... | 14.951476 | 15.674226 | 16.032360 | 15.389942 | 15.329704 | 15.735618 | 15.437993 | 16.389580 | 16.507354 | 15.723169 | ||
| 3 | K | 0.086417 | 0.383767 | 1.117448 | 0.299625 | 0.812439 | 0.679213 | 1.469920 | 0.980958 | 1.493779 | 0.991778 | ... | 16.150289 | 15.991050 | 15.890838 | 16.074022 | 15.879204 | 16.038090 | 15.835617 | 16.697929 | 16.553486 | 16.636269 | ||
| 4 | K | 0.639852 | 0.239320 | 0.436305 | 0.324221 | 0.737537 | 1.076111 | 0.714999 | 0.984073 | 0.807815 | 1.666484 | ... | 15.590641 | 16.223780 | 16.242080 | 15.875550 | 16.203958 | 16.884275 | 16.460586 | 16.806949 | 17.234099 | 17.220744 | ||
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| scenario_4 | Warming ocean | 5 | K | 0.416180 | 0.745776 | 0.600330 | 1.206221 | 1.576249 | 2.283176 | 1.909504 | 2.556301 | 2.289724 | 2.930382 | ... | 46.754790 | 46.691593 | 47.345115 | 47.498748 | 47.657395 | 48.160622 | 47.756959 | 48.130656 | 48.671017 | 49.033449 |
| 6 | K | 0.507068 | 1.195738 | 0.627370 | 1.604990 | 1.945338 | 1.777342 | 1.784241 | 2.679152 | 3.251096 | 2.711349 | ... | 46.639024 | 47.139467 | 47.857915 | 47.898675 | 48.033657 | 48.507038 | 49.064126 | 49.101188 | 49.269901 | 49.219865 | ||
| 7 | K | 0.202576 | 1.173313 | 0.892732 | 1.267043 | 1.981235 | 1.543622 | 2.148345 | 2.086317 | 2.970012 | 3.211165 | ... | 47.269851 | 47.069311 | 48.084094 | 48.036904 | 48.438120 | 49.137475 | 48.552036 | 49.326821 | 49.576255 | 49.516417 | ||
| 8 | K | 0.661070 | 0.684002 | 1.223000 | 0.879542 | 1.260746 | 1.655435 | 2.248054 | 2.929564 | 2.795370 | 3.279841 | ... | 48.010262 | 47.979820 | 48.246170 | 48.434334 | 48.474595 | 49.227165 | 49.322289 | 49.589015 | 49.410732 | 49.741347 | ||
| 9 | K | 0.517006 | 0.489773 | 0.819658 | 1.191446 | 1.417578 | 2.356454 | 2.621087 | 2.982971 | 2.816116 | 3.041367 | ... | 48.229924 | 48.439979 | 48.399689 | 49.250212 | 49.071204 | 49.362236 | 49.144431 | 50.363130 | 49.805935 | 50.388444 |
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.