{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# T8 - Tips and tricks\n", "\n", "This tutorial contains suggestions that aren't essential to follow, but which may make your life easier.\n", "\n", "## Versioning\n", "\n", "Covasim contains a number of built-in tools to make it easier to keep track of where results came from. The simplest of these is that if you save an image using `cv.savefig()` instead of `pl.savefig()`, it will automatically store information about the script and Covasim version that generated it:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import covasim as cv\n", "cv.options.set(dpi=100, show=False, close=True, verbose=0) # Standard options for Jupyter notebook\n", "\n", "sim = cv.Sim()\n", "sim.run()\n", "sim.plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "filename = 'my-figure.png'\n", "cv.savefig(filename) # Save including version information\n", "cv.get_png_metadata(filename) # Retrieve and print information" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This can be extremely useful for figuring out where that intriguing result you generated 3 weeks ago came from!\n", "\n", "This information is also stored in sims and multisims themselves:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(sim.version)\n", "print(sim.git_info)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, the function `cv.check_version()` and `cv.check_save_version()` are useful if you want to ensure that users are running the right version of your code. Placing `cv.check_save_version('2.0.0')` will save a file with the information above to the current folder – again, useful for debugging exactly what changed and when. (You can also provide additional information to it, e.g. to also save the versions of 3rd-party packages you're importing). `cv.check_version()` by itself can be used to provide a warning or even raise an exception (if `die=True`) if the version is not what's expected:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cv.check_version('1.5.0')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with dates\n", "\n", "Dates can be tricky to work with. Covasim comes with a number of built-in features to work with dates. By default, by convention Covasim works with dates in the format `YYYY-MM-DD`, e.g. `'2020-12-01'`. However, it can handle a wide variety of other date and `datetime` objects. In particular, `sim` objects know when they start and end, and can use this to do quite a bit of date math:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim = cv.Sim(start_day='20201122', end_day='2020-12-09 02:14:58.727703')\n", "sim.initialize() # Date conversion happens on initialization\n", "print(sim['start_day'])\n", "print(sim['end_day'])\n", "print(sim.day(sim['end_day'])) # Prints the number of days until the end day, i.e. the length of the sim" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also easily calculate the difference between two dates, or generate a range of dates. These are returned as strings by default, but can be converted to datetime objects via Sciris:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sciris as sc\n", "\n", "print(cv.daydiff('2020-06-01', '2020-07-01', '2020-08-01'))\n", "dates = cv.date_range('2020-04-04', '2020-04-12')\n", "print(dates)\n", "print(sc.readdate(dates))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, one gotcha is that when loading Excel spreadsheets in pandas, dates are loaded in pandas' internal `Timestamp[ns64]` format, which nothing else seems to be able to read. If this happens to you, the solution (as far as Covasim is concerned) is to convert to a `datetime.date`:\n", "\n", "```python\n", "data = pd.read_excel(filename)\n", "data['date'] = data['date'].dt.date\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with dictionaries\n", "\n", "\"I already know how to work with dictionaries\", you say. Yes, you do. But there are a couple tricks that might make things easier.\n", "\n", "Covasim is built on Sciris, which includes containers `odict` and `objdict`. While these are [documented elsewhere](https://sciris.readthedocs.io/en/latest/_autosummary/sciris.sc_odict.odict.html#sciris.sc_odict.odict), a couple examples will serve to illustrate hown ord they work.\n", "\n", "An `odict` is just an ordered dict that you can refer to by *position* as well as by key. For example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mydict = sc.odict(foo=[1,2,3], bar=[4,5,6]) # Assignment is the same as ordinary dictionaries\n", "print('Entry foo:', mydict['foo'])\n", "print('Entry 0:', mydict[0]) # Access by key or by index\n", "for i,key,value in mydict.enumitems(): # Additional methods for iteration\n", " print(f'Item {i} is named {key} and has value {value}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An `objdict` is exactly the same as an odict except it lets you reference keys as if they were attributes:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myobjdict = sc.objdict(foo=[1,2,3], bar=[4,5,6])\n", "print('Entry foo:', myobjdict['foo'])\n", "print('Entry 0:', myobjdict[0]) # Access by key or by index\n", "print('\"Attribute\" foo:', myobjdict.foo)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this approach, you can get all the power and flexibility of dictionaries, while writing code as succinctly as possible. For example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "total_pop = 44_483 # This many total people\n", "\n", "pars= sc.objdict(\n", " pop_type = 'hybrid',\n", " pop_size = 10e3,\n", ")\n", "pars.pop_scale = total_pop/pars.pop_size # Instead of pars['pop_scale'] = total_pop/pars['pop_size'] \n", "sim = cv.Sim(**pars) # It's still a dict, so you can treat it as one!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, Sciris also contains a function called `mergedicts`. This acts very similarly to `dict.update()`, with the main difference being that it returns the result of merging the two dictionaries. This is especially useful for handling keyword arguments in functions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def myfunc(args=None, **kwargs):\n", " defaults = dict(foo=[1,2,3], bar=[4,5,6])\n", " merged_args = sc.mergedicts(defaults, args, kwargs)\n", " print(merged_args)\n", "\n", "myfunc(args=dict(bar=18), other_args='can be anything')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, it merged the default settings, the arguments supplied to the function via the keyword `args`, and then other keywords, into a single dictionary." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" }, "pycharm": { "stem_cell": { "cell_type": "raw", "metadata": { "collapsed": false }, "source": [] } }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }