T2 - Plotting, printing, and saving

This tutorial provides a brief overview of options for plotting results, printing objects, and saving results.

Click here to open an interactive version of this notebook.

Global plotting configuration

Covasim allows the user to set various options that apply to all plots. You can change the font size, default DPI, whether plots should be shown by default, etc. (for the full list, see cv.options.help()). For example, we might want higher resolution, to turn off automatic figure display, close figures after they’re rendered, and to turn off the messages that print when a simulation is running. We can do this using built-in defaults for Jupyter notebooks (and then run a sim) with:

import covasim as cv

cv.options(jupyter=True, verbose=0) # Standard options for Jupyter notebook
sim = cv.Sim()
sim.run()
Covasim 4.0.1 (2026-10-16) — © 2020-2026 by IDM
Sim(n=20000; 2020.03.01—2020.04.30; networks=a; diseases=covid; connectors=crossimmunity)

Printing objects

There are three levels of detail available for most objects (sims, multisims, scenarios, and people). The shortest is brief():

sim.brief()
Sim(<no label>; 2020-03-01 to 2020-04-30; pop: 20000 random; epi: 11873⚙, 21☠)

You can get more detail with summarize():

sim.summarize()
#0. 'a_n_edges':           199694.0
#1. 'n_alive':             19979.0
#2. 'n_female':            10020.0
#3. 'new_deaths':          3.0
#4. 'new_emigrants':       0.0
#5. 'cum_deaths':          21.0
#6. 'pop_scale':           1.0
#7. 'n_susceptible':       12605.0
#8. 'n_infected':          7374.0
#9. 'n_preinfectious':     2602.0
#10. 'n_symptomatic':       2983.0
#11. 'n_severe':            257.0
#12. 'n_critical':          58.0
#13. 'n_recovered':         3945.0
#14. 'n_dead':              3.0
#15. 'n_naive':             8660.0
#16. 'n_tested':            0.0
#17. 'n_diagnosed':         0.0
#18. 'n_known_contact':     0.0
#19. 'n_quarantined':       0.0
#20. 'n_isolated':          0.0
#21. 'n_vaccinated':        0.0
#22. 'prevalence':          0.36908754191901494
#23. 'new_infections':      520.0
#24. 'cum_infections':      11873.0
#25. 'n_infectious':        4772.0
#26. 'new_infectious':      553.0
#27. 'new_symptomatic':     390.0
#28. 'new_severe':          22.0
#29. 'new_critical':        11.0
#30. 'new_recoveries':      379.0
#31. 'cum_infectious':      9271.0
#32. 'cum_symptomatic':     5913.0
#33. 'cum_severe':          322.0
#34. 'cum_critical':        89.0
#35. 'cum_recoveries':      4478.0
#36. 'new_known_deaths':    0.0
#37. 'cum_known_deaths':    0.0
#38. 'n_known_dead':        0.0
#39. 'n_removed':           21.0
#40. 'new_reinfections':    62.0
#41. 'cum_reinfections':    533.0
#42. 'n_exposed':           7374.0
#43. 'n_imports':           0.0
#44. 'new_tests':           0.0
#45. 'cum_tests':           0.0
#46. 'new_diagnoses':       0.0
#47. 'cum_diagnoses':       0.0
#48. 'new_doses':           0.0
#49. 'cum_doses':           0.0
#50. 'new_vaccinated':      0.0
#51. 'cum_vaccinated':      0.0
#52. 'new_quarantined':     0.0
#53. 'cum_quarantined':     0.0
#54. 'new_isolated':        0.0
#55. 'cum_isolated':        0.0
#56. 'test_yield':          0.0
#57. 'r_eff':               1.058728828217512
#58. 'incidence':           0.041253470844902815
#59. 'rel_test_yield':      0.0
#60. 'frac_vaccinated':     0.0
#61. 'doubling_time':       13.897517771712964
#62. 'pop_nabs':            2.137901544570923
#63. 'pop_protection':      0.16149389743804932
#64. 'pop_symp_protection': 0.06619777530431747
#65. 't':                   60

Finally, to show the full object, including all methods and attributes, use disp():

sim.disp()
<covasim.sim.Sim at 0x7f42163e0ec0>
[<class 'covasim.sim.Sim'>, <class 'covasim.compat.V3Sim'>, <class 'starsim.sim.Sim'>, <class 'starsim.modules.Base'>]
————————————————————————————————————————————————————————————————————————
Methods:
  _add_network()          finalize()              make_transtree()        
  _get_ia()               finalize_results()      plot()                  
  _is_v3_saved()          finish_step()           plot_result()           
  _load_location()        get_analyzer()          products()              
  _make_contacts()        get_analyzers()         profile()               
  _restore_orig()         get_intervention()      rescale()               
  _v3_covid()             get_interventions()     reset_layer_pars()      
  _v3_key_error()         get_module()            reset_time_pars()       
  _v3_networks()          get_modules()           result_keys()           
  add_module()            init()                  result_scale()          
  brief()                 init_data()             run()                   
  calibrate()             init_dists()            run_one_step()          
  check_method_calls()    init_module_attrs()     save()                  
  check_results_ready()   init_modules_post()     set_diagnostics()       
  compute_fit()           init_modules_pre()      set_seed()              
  compute_gen_time()      init_people()           shrink()                
  compute_r_eff()         init_people_vals()      start_step()            
  compute_summary()       init_rescale()          step()                  
  copy()                  init_results()          summarize()             
  cprofile()              init_time()             to_df()                 
  date()                  initialize()            to_excel()              
  day()                   layer_keys()            to_json()               
  disp()                  load()                  to_yaml()               
  export_pars()           lock_pars()             update_pars()           
  export_results()        make_age_histogram()    
————————————————————————————————————————————————————————————————————————
Properties:
  current_scale           modules                 scaled_pop_size         
  datevec                 n                       ti                      
  dt                      now                     timevec                 
  label                   npts                    tvec                    
  label_to_name           rescale_vec             
————————————————————————————————————————————————————————————————————————
       _age_data: None
   _bridged_keys: ['n_susceptible', 'n_infected', 'n_preinfectious',
                  'n_symptomati [...]
 _household_size: None
      _orig_pars: None
       _orig_sim: b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xed=\tx\x
                  14U\xd2\xb9\ [...]
_v3_changed_pars: []
    _v3_contacts: {'a': 20}
 _v3_dur_applied: {'exp2inf': {'dist': 'lognormal_int', 'par1': 4.5,
                  'par2': 1.5}, [...]
        _v3_pars: {'location': None, 'scaled_pop': None, 'variants':
                  [], 'dur': {' [...]
       analyzers: ndict()
        complete: True
      connectors: #0. 'crossimmunity': crossimmunity(pars=[immunity,
                  disease]; sta [...]
         created: datetime.datetime(2026, 10, 16, 16, 14, 30, 680719)
          custom: ndict()
            data: None
    demographics: ndict()
     diagnostics: None
        diseases: #0. 'covid': covid(pars=[init_prev, beta,
                  beta_layer, rel_beta,  [...]
           dists: <starsim.distributions.Dists at 0x7f42163e2e40>
                  [<class 'starsim [...]
     dynam_layer: {'a': 0}
         elapsed: 0.6309897899627686
        filename: None
        git_info: {'covasim': {'version': '4.0.1', 'branch': 'Detached
                  head (no br [...]
     initialized: True
   interventions: ndict()
            loop: Loop(n=1098, funcs=18, npts=61, index=1098)
        metadata: #0. 'version':     '3.7.2'
                  #1. 'versiondate': '2026-10-06'
                  #2. ' [...]
        networks: #0. 'a': a:RandomNet(n_edges=199635;
                  pars=[n_contacts, dur, beta [...]
            pars: #0. 'label':             ''
                  ———————————————————————————————————— [...]
          people: People(n=20000; alive=19979; age=38.3±22.0)
        pop_type: 'random'
         results: Results(Sim)
                  0. timevec: array(start=2020.03.01, stop=2020.04.30
                  [...]
   results_ready: True
     rng_rescale: ss.choose_n(n=1, weights=None)
         simfile: None
   stopping_func: None
         summary: #0. 'a_n_edges':           199694.0
                  #1. 'n_alive':             1 [...]
               t: Timeline(2020.03.01-2020.04.30; dt=days(1);
                  now=2020.04.30; ti=6 [...]
       timelimit: None
           timer: <sciris.sc_datetime.timer at 0x7f42163e1940>
                  Timings:
                  #0. '(0)': [...]
         verbose: 0
         version: '4.0.1'
   _v3_init_keys: ['pop_size', 'pop_type', 'pop_infected', 'n_days',
                  'start_day',  [...]
————————————————————————————————————————————————————————————————————————

Plotting options

While a sim can be plotted using default settings simply by sim.plot(), this is just a small fraction of what’s available. First, note that results can be plotted directly using e.g. Matplotlib. You can see what quantities are available for plotting with sim.results.keys() (remember, it’s just a dict). A simple example of plotting using Matplotlib is:

import pylab as pl # Shortcut for import matplotlib.pyplot as plt
pl.plot(sim.results['date'], sim.results['new_infections'])

However, as you can see, this isn’t ideal since the default formatting is…not great. (Also, note that each result is a Result object, not a simple Numpy array; like a pandas dataframe, you can get the array of values directly via e.g. sim.results.new_infections.values.)

An alternative, if you only want to plot a single result, such as new infections, is to use the plot_result() method:

sim.plot_result('new_infections')

You can also select one or more quantities to plot with the first (to_plot) argument, e.g.

sim.plot(to_plot=['new_infections', 'cum_infections'])

Another useful option is to plot an overview of everything in a simulation. We can do this with the to_plot='overview' argument. It’s quite a lot of information so we might also want to make a larger figure for it, which we can do by passing additional arguments (which, if recognized, are passed to pl.figure()). We can also change the date format between 'sciris' (the default), 'concise' (better for cramped plots), and 'matplotlib' (Matplotlib’s default):

sim.plot('overview', n_cols=5, figsize=(20,20), dateformat='concise', dpi=50) # NB: dateformat='concise' is already the default for >2 columns

While we can save this figure using Matplotlib’s built-in savefig(), if we use Covasim’s cv.savefig() we get a couple advantages:

cv.savefig(filename='my-fig.png')
'my-fig.png'
<Figure size 672x480 with 0 Axes>

First, it saves the figure at higher resolution by default (which you can adjust with the dpi argument). But second, it stores information about the code that was used to generate the figure as metadata, which can be loaded later. Made an awesome plot but can’t remember even what script you ran to generate it, much less what version of the code? You’ll never have to worry about that again.

cv.get_png_metadata('my-fig.png')
Covasim version: 4.0.1
Covasim branch: Detached head (no branch)
Covasim hash: 75f8f6f
Covasim date: 2026-10-16T09:04:49-07:00
Covasim caller branch: Detached head (no branch)
Covasim caller hash: 75f8f6f
Covasim caller date: 2026-10-16T09:04:49-07:00
Covasim caller filename: /home/runner/work/covasim/covasim/covasim/misc.py
Covasim current time: 2026-Oct-16 16:14:34
Covasim calling file: /tmp/ipykernel_4307/1033588508.py

Customizing plots

We saw above how to set default plot configuration options for Jupyter. Covasim provides a lot of flexibility in customizing the appearance of plots as well. There are three different levels at which you can set plotting options: global, just for Covasim, or just for the current plot. To give an example with changing the figure DPI: - Change the setting globally (for both Covasim and Matplotlib): sc.options(dpi=150) or pl.rc('figure', dpi=150) (where sc is import sciris as sc) - Change for Covasim plots, but not for Matplotlib plots: cv.options(dpi=150) - Change for the current Covasim plot, but not other Covasim plots: sim.plot(dpi=150)

The easiest way to change the style of Covasim plots is with the style argument. For example, to plot using a built-in Matplotlib style would simply be:

sim.plot(style='ggplot')

In addition to the default style ('covasim'), there is also a “simple” style. You can combine built-in styles with additional overrides, including any valid Matplotlib commands:

sim.plot(style='simple', font='Rosario', legend_args={'frameon':True}, style_args={'ytick.direction':'in'})

(Note: Covasim comes bundled with two fonts, Mulish (sans-serif, the default) and Rosario (serif).)

Although most style handling is done automatically, you can also use it yourself in a with block, e.g.:

with cv.options.with_style(fontsize=6):
    sim.plot() # This will have 6 point font
    pl.figure(); pl.plot(pl.rand(20), 'o') # So will this

Saving options

Saving sims is also pretty simple. The simplest way to save is simply

sim.save('my-awesome-sim.sim')
'/home/runner/work/covasim/covasim/docs/tutorials/my-awesome-sim.sim'

Technically, this saves as a gzipped pickle file (via sc.saveobj() using the Sciris library). By default this does not save the people in the sim since they are very large (and since, if the random seed is saved, they can usually be regenerated). If you want to save the people as well, you can use the keep_people argument. For example, here’s what it would look like to create a sim, run it halfway, save it, load it, change the overall transmissibility (beta), and finish running it:

sim_orig = cv.Sim(start_day='2020-04-01', end_day='2020-06-01', label='Load & save example')
sim_orig.run(until='2020-05-01')
sim_orig.save('my-half-finished-sim.sim') # Note: Covasim always saves the people if the sim isn't finished running yet

sim = cv.load('my-half-finished-sim.sim')
sim['beta'] *= 0.3
sim.run()
sim.plot(['new_infections', 'n_infectious', 'cum_infections'])

Aside from saving the entire simulation, there are other export options available. You can export the results and parameters to a JSON file (using sim.to_json()), but probably the most useful is to export the results to an Excel workbook, where they can easily be stored and processed with e.g. Pandas:

import pandas as pd

sim.to_excel('my-sim.xlsx')
df = pd.read_excel('my-sim.xlsx')
print(df)
Object saved to /home/runner/work/covasim/covasim/docs/tutorials/my-sim.xlsx.
         date   t  a_n_edges  prevalence_by_variant_wild  \
0  2020-04-01   0     200089                    0.001000   
1  2020-04-02   1     200089                    0.001000   
2  2020-04-03   2     200089                    0.001150   
3  2020-04-04   3     200089                    0.001250   
4  2020-04-05   4     200089                    0.001450   
..        ...  ..        ...                         ...   
57 2020-05-28  57     199832                    0.015960   
58 2020-05-29  58     199832                    0.015509   
59 2020-05-30  59     199832                    0.015609   
60 2020-05-31  60     199832                    0.015460   
61 2020-06-01  61     199811                    0.014910   

    incidence_by_variant_wild  new_infections_by_variant_wild  \
0                    0.000000                               0   
1                    0.000000                               0   
2                    0.000150                               3   
3                    0.000100                               2   
4                    0.000200                               4   
..                        ...                             ...   
57                   0.000813                              16   
58                   0.000661                              13   
59                   0.001118                              22   
60                   0.001016                              20   
61                   0.000762                              15   

    cum_infections_by_variant_wild  new_symptomatic_by_variant_wild  \
0                               20                                0   
1                               20                                0   
2                               23                                0   
3                               25                                3   
4                               29                                1   
..                             ...                              ...   
57                            1447                               12   
58                            1460                               22   
59                            1482                               14   
60                            1502                               15   
61                            1517                               14   

    cum_symptomatic_by_variant_wild  new_severe_by_variant_wild  ...  \
0                                 0                           0  ...   
1                                 0                           0  ...   
2                                 0                           0  ...   
3                                 3                           0  ...   
4                                 4                           0  ...   
..                              ...                         ...  ...   
57                              924                           1  ...   
58                              946                           1  ...   
59                              960                           0  ...   
60                              975                           1  ...   
61                              989                           3  ...   

    cum_isolated  test_yield     r_eff  incidence  rel_test_yield  \
0              0           0  3.099759   0.000000               0   
1              0           0  3.381555   0.000000               0   
2              0           0  3.391480   0.000150               0   
3              0           0  3.254062   0.000100               0   
4              0           0  3.082368   0.000200               0   
..           ...         ...       ...        ...             ...   
57             0           0  0.668589   0.000813               0   
58             0           0  0.702417   0.000661               0   
59             0           0  0.789437   0.001118               0   
60             0           0  0.875258   0.001016               0   
61             0           0  0.674594   0.000762               0   

    frac_vaccinated  doubling_time  pop_nabs  pop_protection  \
0                 0            NaN  0.000336        0.000000   
1                 0            NaN  0.000673        0.000000   
2                 0            NaN  0.001053        0.000000   
3                 0       9.318851  0.001438        0.000000   
4                 0       5.596463  0.001916        0.000000   
..              ...            ...       ...             ...   
57                0      30.000000  0.340383        0.043758   
58                0      30.000000  0.344928        0.044575   
59                0      30.000000  0.349236        0.045283   
60                0      30.000000  0.353284        0.046032   
61                0      30.000000  0.356701        0.047024   

    pop_symp_protection  
0              0.000000  
1              0.000000  
2              0.000000  
3              0.000000  
4              0.000000  
..                  ...  
57             0.018229  
58             0.018590  
59             0.018914  
60             0.019270  
61             0.019668  

[62 rows x 79 columns]