T2 - Plotting, printing, and saving

In this tutorial we take one finished run and get four things out of it: a single-panel figure, an HPV summary figure, a table you can hand to a spreadsheet, and a saved file you can reopen next week.

The run we will use

import numpy as np
import pandas as pd
import starsim as ss
import hpvsim as hpv

sim = hpv.Sim(
    location       = 'nigeria',
    genotypes      = [16, 18, 'hi5', 'ohr'],
    n_agents       = 2000,
    start          = 1990,
    stop           = 2040,
    ms_agent_ratio = 25,
    verbose        = 0,                                     # No progress messages
    analyzers      = [hpv.by_age(['cancers', 'hpv_prevalence'])],
    label          = 'Nigeria baseline',
)
sim.run()
Sim(Nigeria baseline; n=2000; 1990—2040; demographics=births, deaths, agemigration; networks=sexualnetwork; diseases=hpv16, hpv18, hi5, ohr; connectors=crossimmunity, _exclusiveseeder; analyzers=all_hpv, by_age)

verbose=0 keeps the run silent, and verbose=-1 silences the setup messages too. Setting verbose=0.25 prints an update once a simulated year, which is what you want when a run is long enough that you start wondering whether it is stuck.

One result at a time

Name any result and you get one panel:

fig = sim.plot('asr_cancer_incidence')
Figure(768x576)

Pass a list of fully-qualified names for several panels. Names are the module name and the result name joined by an underscore:

fig = sim.plot(['all_hpv_prevalence', 'all_hpv_cum_cancers',
                'all_hpv_asr_cancer_mortality'])
Figure(768x576)

Calling sim.plot() with no argument plots every result from every module, which for a four-genotype run is over sixty panels. Useful when you are hunting a problem, unreadable otherwise. sim.plot('all_hpv') narrows it to the pooled HPV results.

The HPV summary figure

hpv.plot_sim draws the four panels an HPV modeller usually wants first: cumulative cancers, HPV prevalence by age, cancers by age, and which genotypes are causing the cancers. It needs a hpv.by_age analyzer recording cancers and a prevalence, which is why we attached one above:

fig = hpv.plot_sim(sim)
/home/robyn/hpvsim/hpvsim/plotting.py:415: UserWarning: Tight layout not applied. tight_layout cannot make Axes height small enough to accommodate all Axes decorations.
  fig.tight_layout()

Read the bottom-right panel against genotyping studies for your country. If your run puts almost everything in the pooled hi5 and ohr groups while your data puts it in HPV16 and HPV18, the per-genotype transmission parameters need attention before you trust anything downstream.

Genotypes over time

fig = hpv.plot_by_genotype(sim, key='cum_cancers')

The same numbers as a table, with one column per genotype:

hpv.results_by_genotype(sim, key='cum_cancers', normalize=True).tail(3).round(3)
hpv16 hpv18 hi5 ohr
year
2039.498630 0.538 0.188 0.123 0.152
2039.750685 0.537 0.187 0.123 0.153
2040.000000 0.537 0.187 0.124 0.153

A table you can share

Every result shares the time axis in sim.results.timevec, so assembling a tidy table is a few lines. Slicing a result with [:] gives you the underlying array:

df = pd.DataFrame({
    'year':           sim.results.timevec.years,
    'hpv_prevalence': sim.results.all_hpv.prevalence[:],
    'new_cancers':    sim.results.all_hpv.new_cancers[:],
    'incidence':      sim.results.all_hpv.asr_cancer_incidence[:],
})
df.to_csv('my-nigeria-results.csv', index=False)
df.tail(4).round(3)
year hpv_prevalence new_cancers incidence
197 2039.249 0.057 9580.697 30.478
198 2039.499 0.056 9580.697 38.016
199 2039.751 0.054 1916.139 7.542
200 2040.000 0.052 3832.279 17.363

Counts like new_cancers are per timestep, not per year. At the default quarter-year timestep there are four rows per calendar year, so a yearly total is the sum of all four. annualize() does that for you and returns one row per year:

annual = sim.results.all_hpv.new_cancers.annualize()
annual_years = np.floor(annual.timevec.years).astype(int)
print('Cancers in 2035:', round(float(annual.values[annual_years == 2035][0])))
Cancers in 2035: 36407

An annualized timestamp sits in the middle of its year, so match on np.floor of the year rather than on equality.

Printing a run

summarize() prints the last value of every result — a quick check that nothing came out as zero or a NaN:

sim.summarize()
#0. 'births_new':                      1691636.5531194028
#1. 'births_cumulative':               154981125.07249495
#2. 'births_cbr':                      32.10674071057154
#3. 'deaths_new':                      706874.3331293532
#4. 'deaths_cumulative':               65128007.29509699
#5. 'deaths_cmr':                      13.530661552925302
#6. 'agemigration_new_immigrants':     1987525.0477254898
#7. 'agemigration_new_emigrants':      2062667.771647059
#8. 'crossimmunity_n_rel_sev_sampled': 222498110.22170147
#9. 'sexualnetwork_n_edges':           56927454.72306467
#10. 'hpv16_n_susceptible':             183389001.71875986
#11. 'hpv16_n_infected':                8117900.868094551
#12. 'hpv16_n_precin':                  7324790.9998480715
#13. 'hpv16_n_cin':                     793110.011890725
#14. 'hpv16_n_cancerous':               78208.9844069465
#15. 'hpv16_n_undetected_cancerous':    11754.227899809322
#16. 'hpv16_n_latent':                  0.0
#17. 'hpv16_prevalence':                0.04425645387999213
#18. 'hpv16_new_infections':            1460937.1753482588
#19. 'hpv16_cum_infections':            293648372.245
#20. 'hpv16_new_cancers':               3012.4380673786736
#21. 'hpv16_new_undetected_cancers':    3231.697808784479
#22. 'hpv16_new_cancer_deaths':         2030.5357985099631
#23. 'hpv16_cum_cancers':               605500.0515431139
#24. 'hpv16_cum_cancer_deaths':         408137.6955005028
#25. 'hpv16_sum_age_at_cancer':         133819.88265808206
#26. 'hpv16_sum_age_at_cancer_death':   95902.3387437911
#27. 'hpv16_new_reactivations':         0.0
#28. 'hpv18_n_susceptible':             188677858.59311283
#29. 'hpv18_n_infected':                2829041.591791663
#30. 'hpv18_n_precin':                  2575386.849075121
#31. 'hpv18_n_cin':                     253654.8786472959
#32. 'hpv18_n_cancerous':               29838.39256771899
#33. 'hpv18_n_undetected_cancerous':    3937.1421393630458
#34. 'hpv18_n_latent':                  0.0
#35. 'hpv18_prevalence':                0.018033490385270433
#36. 'hpv18_new_infections':            553392.5156865672
#37. 'hpv18_cum_infections':            111231895.653
#38. 'hpv18_new_cancers':               1048.633511884597
#39. 'hpv18_new_undetected_cancers':    1124.8977672943859
#40. 'hpv18_new_cancer_deaths':         877.0389372125721
#41. 'hpv18_cum_cancers':               210775.33588880414
#42. 'hpv18_cum_cancer_deaths':         176284.82637972702
#43. 'hpv18_sum_age_at_cancer':         45933.967657258814
#44. 'hpv18_sum_age_at_cancer_death':   40566.135768537766
#45. 'hpv18_new_reactivations':         0.0
#46. 'hi5_n_susceptible':               189389127.523724
#47. 'hi5_n_infected':                  2117772.6293605873
#48. 'hi5_n_precin':                    1935462.9931778673
#49. 'hi5_n_cin':                       182309.67656135582
#50. 'hi5_n_cancerous':                 20410.221746466847
#51. 'hi5_n_undetected_cancerous':      2878.9756204115424
#52. 'hi5_n_latent':                    0.0
#53. 'hi5_prevalence':                  0.015322361835658086
#54. 'hi5_new_infections':              407775.4497587064
#55. 'hi5_cum_infections':              81962865.4015
#56. 'hi5_new_cancers':                 695.9113306143236
#57. 'hi5_new_undetected_cancers':      705.4443625405472
#58. 'hi5_new_cancer_deaths':           581.5149474996402
#59. 'hi5_cum_cancers':                 139878.17745347897
#60. 'hi5_cum_cancer_deaths':           116884.50444742761
#61. 'hi5_sum_age_at_cancer':           32899.31720165767
#62. 'hi5_sum_age_at_cancer_death':     29718.567271679593
#63. 'hi5_new_reactivations':           0.0
#64. 'ohr_n_susceptible':               187936236.3171062
#65. 'ohr_n_infected':                  3570663.8773442633
#66. 'ohr_n_precin':                    3260754.697209244
#67. 'ohr_n_cin':                       309909.28207242984
#68. 'ohr_n_cancerous':                 22774.41396780912
#69. 'ohr_n_undetected_cancerous':      3546.2878534715305
#70. 'ohr_n_latent':                    0.0
#71. 'ohr_prevalence':                  0.024236729379236183
#72. 'ohr_new_infections':              690429.8526890548
#73. 'ohr_cum_infections':              138776400.3905
#74. 'ohr_new_cancers':                 857.9728733601248
#75. 'ohr_new_undetected_cancers':      877.0389372125721
#76. 'ohr_new_cancer_deaths':           629.1801071307583
#77. 'ohr_cum_cancers':                 172452.5475453851
#78. 'ohr_cum_cancer_deaths':           126465.20153328232
#79. 'ohr_sum_age_at_cancer':           39703.79281520582
#80. 'ohr_sum_age_at_cancer_death':     31911.359643167325
#81. 'ohr_new_reactivations':           0.0
#82. 'all_hpv_n_infected':              14833597.612435136
#83. 'all_hpv_n_precin':                13451155.96935184
#84. 'all_hpv_n_cin':                   1511948.1894510922
#85. 'all_hpv_n_cancerous':             151231.99990592475
#86. 'all_hpv_n_undetected_cancerous':  22116.634553595486
#87. 'all_hpv_n_latent':                0.0
#88. 'all_hpv_new_infections':          3112534.9934825874
#89. 'all_hpv_cum_infections':          625619533.69
#90. 'all_hpv_new_cancers':             5614.9557832377195
#91. 'all_hpv_new_undetected_cancers':  5939.078875831984
#92. 'all_hpv_new_cancer_deaths':       4118.269790352933
#93. 'all_hpv_cum_cancers':             1128606.112430782
#94. 'all_hpv_cum_cancer_deaths':       827772.2278609397
#95. 'all_hpv_sum_age_at_cancer':       252356.96033220438
#96. 'all_hpv_sum_age_at_cancer_death': 198098.40142717576
#97. 'all_hpv_new_reactivations':       0.0
#98. 'all_hpv_n_susceptible':           176824533.76460895
#99. 'all_hpv_prevalence':              0.0882726178425758
#100. 'all_hpv_cum_infections_unique':   116980314.03299999
#101. 'all_hpv_asr_cancer_incidence':    32.72696987166942
#102. 'all_hpv_asr_cancer_mortality':    0.0
#103. 'all_hpv_cancer_incidence':        22.49765513785443
#104. 'by_age_cancers_0_5':              0.0
#105. 'by_age_cancers_5_10':             0.0
#106. 'by_age_cancers_10_15':            0.0
#107. 'by_age_cancers_15_20':            19.06606385244722
#108. 'by_age_cancers_20_25':            266.92489215859547
#109. 'by_age_cancers_25_30':            610.1140415026454
#110. 'by_age_cancers_30_35':            886.5719638117989
#111. 'by_age_cancers_35_40':            886.5719584848019
#112. 'by_age_cancers_40_45':            714.977374934449
#113. 'by_age_cancers_45_50':            619.6470414668881
#114. 'by_age_cancers_50_55':            514.7836956054252
#115. 'by_age_cancers_55_60':            352.72215996228624
#116. 'by_age_cancers_60_65':            352.72215285962386
#117. 'by_age_cancers_65_70':            200.19364559137728
#118. 'by_age_cancers_70_75':            114.39637423635529
#119. 'by_age_cancers_75_80':            28.59909400300522
#120. 'by_age_cancers_80_85':            28.599092227339614
#121. 'by_age_cancers_85_90':            0.0
#122. 'by_age_cancers_90_95':            0.0
#123. 'by_age_cancers_95_100':           0.0
#124. 'by_age_hpv_prevalence_0_5':       0.0
#125. 'by_age_hpv_prevalence_5_10':      0.0
#126. 'by_age_hpv_prevalence_10_15':     0.005620187758039498
#127. 'by_age_hpv_prevalence_15_20':     0.0933949602350815
#128. 'by_age_hpv_prevalence_20_25':     0.1942416571117752
#129. 'by_age_hpv_prevalence_25_30':     0.20489501749253392
#130. 'by_age_hpv_prevalence_30_35':     0.1933895565977144
#131. 'by_age_hpv_prevalence_35_40':     0.186722213615529
#132. 'by_age_hpv_prevalence_40_45':     0.20556477101436302
#133. 'by_age_hpv_prevalence_45_50':     0.17172362384808004
#134. 'by_age_hpv_prevalence_50_55':     0.1250138446429179
#135. 'by_age_hpv_prevalence_55_60':     0.08072598712213004
#136. 'by_age_hpv_prevalence_60_65':     0.05418341685160624
#137. 'by_age_hpv_prevalence_65_70':     0.034200778409991583
#138. 'by_age_hpv_prevalence_70_75':     0.023633606593467443
#139. 'by_age_hpv_prevalence_75_80':     0.011985523057452853
#140. 'by_age_hpv_prevalence_80_85':     0.003675248431023303
#141. 'by_age_hpv_prevalence_85_90':     0.00044007400115627553
#142. 'by_age_hpv_prevalence_90_95':     0.0
#143. 'by_age_hpv_prevalence_95_100':    0.0
#144. 'n_alive':                         221111769.02284333
#145. 'n_female':                        125238779.77527861
#146. 'new_deaths':                      809592.7544303482
#147. 'new_emigrants':                   583421.566925373
#148. 'cum_deaths':                      162728143.6405

To see what a genotype’s natural-history parameters are set to:

print(sim.diseases.hpv16.pars)
#0. 'init_prev':        ss.bernoulli(p=<function
_ExclusiveSeeder.for_genotype.<locals>.callback at 0x76460a99e2a0>)
#1. 'beta':             0.25
#2. 'dur_precin':       ss.lognorm_ex(mean=3.0, std=9.0)
#3. 'dur_cin':          ss.lognorm_ex(mean=5.0, std=20.0)
#4. 'dur_cancer':       ss.lognorm_ex(mean=8.0, std=3.0)
#5. 'dur_inf_male':     ss.lognorm_ex(mean=1.0, std=1.0)
#6. 'cin_fn':
    #0. 'form':   'logf2'
    #1. 'k':      0.3
    #2. 'x_infl': 0
    #3. 'ttc':    50
#7. 'cancer_fn':
    #0. 'method':         'cin_integral'
    #1. 'transform_prob': 0.002
    #2. 'form':           'logf2'
    #3. 'k':              0.3
    #4. 'x_infl':         0
    #5. 'ttc':            50
#8. 'imm_init':         ss.uniform(low=0.5, high=0.95)
#9. 'cell_imm_init':    ss.uniform(low=0.3, high=0.9)
#10. 'age_risk':
    #0. 'age':     30
    #1. 'age_end': 50
    #2. 'risk':    2
#11. 'rel_beta':         1.0
#12. 'transf2m':         1.0
#13. 'transm2f':         2.0
#14. 'sero_prob':        0.75
#15. 'ms_agent_ratio':   25
#16. 'hpv_control_prob': 0.0
#17. 'hpv_reactivation': probperyear(0.025)
#18. 'dur_undetected':   ss.lognorm_ex(mean=1, std=1)

Saving

Save a figure with Matplotlib:

import pylab as plt
sim.plot('asr_cancer_incidence')
plt.gcf().savefig('my-incidence.png', dpi=150)
Figure(768x576)

<Figure size 672x480 with 0 Axes>

Save a whole run with sim.save() and reopen it with ss.load(). Saving strips the random number generators and internal cross-references first, so the file stays around a megabyte. You can stop a run partway, save, reload, and carry on:

partial = hpv.Sim(location='nigeria', genotypes=[16, 18], n_agents=2000,
                  start=1990, stop=2030, verbose=0, label='Paused run')
partial.run(until=2010)
partial.save('my-paused-run.sim')

resumed = ss.load('my-paused-run.sim')
resumed.run()
print('Resumed and finished in', float(resumed.results.timevec.years[-1]))
Resumed and finished in 2030.0

Changing how figures look

hpv.options sets defaults for every HPVsim figure — resolution, font size, and plot style. hpv.options.help() lists them all. The two built-in styles are 'hpvsim' (the default) and 'simple', and any Matplotlib style name also works:

fig = sim.plot('asr_cancer_incidence', style='simple')
Figure(768x576)

To change a setting for one block of code only, use with_style:

import pylab as plt
with hpv.options.with_style(fontsize=8):
    fig = sim.plot('asr_cancer_incidence')
Figure(768x576)