Dev Site — You are viewing the development build. Go to Main Site

  • English
  • Français
  1. 2 Assemblage et gestion des données
  2. 2.3 Données de cas de routine (DHIS2)
  3. Health facility reporting rate
  • Bibliothèque de code pour l'adaptation infranationale
    Version française
  • 1 Pour commencer
    • 1.1 À propos et comment nous contacter
    • 1.2 Pour tous
    • 1.3 Pour l’équipe SNT
    • 1.4 Pour les analystes
    • 1.5 Produire des résultats de haute qualité
  • 2 Assemblage et gestion des données
    • 2.1 Utilisation des shapefiles
      • Aperçu des données spatiales
      • Examiner les données du fichier shapefile
      • Shapefile management and customization
      • Merge shapefile with excel
    • 2.2 Formations sanitaires
      • Health facility active/inactive status
      • Health facility coordinates
      • Master facility lists
    • 2.3 Données de cas de routine (DHIS2)
      • Health facility reporting rate
      • Outlier detection methods
      • Imputation of missing data
      • Final database
      • Data extraction from DHIS2
      • Import dataset
      • Outlier correction
      • Quality control/checks
    • 2.4 Données du stock
      • lmis
    • 2.5 Données démographiques
      • Données démographiques nationales
      • Raster de population WorldPop
    • 2.6 Enquêtes nationales auprès des ménages
      • DHS Data Overview and Preparation
      • All-Cause Child Mortality
      • Extraction of ITN ownership, access, and usage
      • Extracion of prevalence data
      • Calculation of treatment-seeking data
    • 2.7 Données entomologiques
    • 2.8 Données climatiques et environnementales
      • Extraction de données climatiques et environnementales à partir de données raster
    • 2.9 Données modélisées
      • Generating spatial modeled estimates
      • Travailler avec les estimations modélisées géospatiales
      • Modeled Estimates of Entomological Indicators
      • Mortality estimates from IHME
  • 3 Stratification
    • 3.1 Stratification épidémiologique
    • 3.2 Stratification des déterminants de la transmission du paludisme
  • 4 Revue des interventions passées
    • 4.1 Prise en charge des cas
    • 4.2 Interventions de routine
    • 4.3 Interventions de campagne
    • 4.4 Autres interventions
  • 5 Ciblage des interventions
  • 6 Analyse rétrospective
  • 7 Microstratification urbaine

On this page

  • Overview
  • Defining and Calculating Reporting Rate for Routine Indicators
    • What is reporting rate?
    • Establishing the denominator: Which facilities are expected to report?
    • Calculating reporting rates
      • Worked example
    • Weighted reporting rates
      • Calculating weighted reporting rates
      • How do I know whether to use unweighted or weighted reporting rate?
  • Step-by-Step
    • Step 1: Import relevant packages and pre-processed routine data
    • Step 2: Import your denominator dataframe - dfden
    • Step 3: Define function to calculate reporting rate
    • Step 4: Calculate reporting rate for a given indicator, at a given admin unit level
    • Step 5: Visualise indicator-specific reporting rates
      • Step 5.1 Multiple indicators, nationally
      • Step 5.2 Single indicator, by admin unit
  • Full code
  1. 2 Assemblage et gestion des données
  2. 2.3 Données de cas de routine (DHIS2)
  3. Health facility reporting rate

Health facility reporting rate

Overview

In the SNT workflow, routine surveillance data are often used to calculate key indicators such as malaria incidence, test positivity rate, and confirmed case counts. To interpret and use this data properly, we need to first assess their quality and completeness. One important assessment is of what proportion of health facilities are reporting consistently and completely across geography and time.

Reporting rates provide a simple and essential metric to evaluate the completeness of routine data. They help identify where gaps in reporting may affect the reliability of indicators used in decision-making and where routine surveillance should be strengthened. Reporting rates can also be used to estimate the number of additional cases (or other count indicator) that would have also been included in routine surveillance, if all facilities had reported.

This section outlines how to calculate, inspect, and save reporting rates using a reproducible approach, while grounding all choices in dialogue with the national malaria program and SNT team.

Objectives
  • Understand how to calculate reporting rates from routine health facility data
  • Visualize and interpret reporting rate patterns at any admin unit level
  • Compile and save validated reporting rate outputs for use in later SNT workflow steps

Defining and Calculating Reporting Rate for Routine Indicators

To ensure routine data can be used reliably in the SNT workflow, we need a clear and consistent method for calculating reporting rates across facilities and time. This section introduces a process to calculate monthly reporting rates for any routinely reported indicator.

What is reporting rate?

Reporting rate is the proportion of entities, such as health facilities or community health workers, in a given admin unit that reported on an indicator during a time period of interest.

In the example on this page, we are using monthly data from DHIS2, so we calculate monthly reporting rates. However, you should calculate reporting rate for the relevant reporting period in your dataset. For example, if you are analyzing weekly surveillance data, your reporting rate should be calculated on a weekly basis.

When presenting reporting rates, it is important to specify what indicator(s) are used to define the entity as reporting. In SNT, it is best practice to re-calculate reporting rate for each indicator of interest, as reporting practice may vary across indicators within the same facility. For example, a facility may prioritize reporting confirmed cases, as their stock replenishment depends on showing consistent reporting, but neglect to report all-cause outpatient visits.

An overall reporting rate can also be calculated for each entity in a given time period. In this case, a facility may only be defined as reporting if it reports suspected cases, tested cases, confirmed cases, and treated cases. This aggregated reporting rate should be at most the minimum of the reporting rate of each individual indicator.

Establishing the denominator: Which facilities are expected to report?

Before evaluating the proportion of facilites that reported in a given reporting period, we need to first determine the number of facilities that should report. To avoid underestimating the reporting rate and gaining and inaccurate assessment of the quality of surveillance, the SNT team may, for example, consider:

  • When calculating reporting rate for confirmed malaria cases, exclude facility types that do not test or treat malaria. For example, HIV clinics or maternity wards.

  • When calculating reporting rate for malaria admissions, exclude facility types that only handle outpatients, for example community health workers or health posts.

  • When calculating reporting rate for an indicator, exclude facilities that have closed, that are not yet active, or are temporarily nonfunctional. This avoids penalizing newly opened facilities that weren’t expected to report in earlier months, or facilities that are permanently or temporarily closed and therefore are not expected to report.

Up-to-date master facility lists (MFL) that track facility type and activity status are very helpful for determining which health facilities should be included in the denominator for reporting rate of each indicator, for each reporting period. In the absence of an MFL, or official determination of activity status, it is still possible to infer which health facilities should be excluded.

For more guidance on how to consider active vs inactive facilities, please see the code library page Determining active and inactive status.

Code for excluding by specific facility type is included on this page.

Consult SNT team

Consult the SNT team to understand how to determine which facilities, if any, should not be included in the denominator for reporting rate. National practices vary, and the surveillance focal person on the SNT team should explain what would be appropriate for each indicator.

Calculating reporting rates

Once health facility activity status has been established, reporting rates can be calculated. These rates reflect the proportion of expected facilities that submitted valid data for a given indicator in a given time period.

For each indicator of interest, reporting rate is defined as:

\[ \text{Indicator Reporting Rate}_{a,t} = \frac{o_{a,t}}{e_{a,t}} \]

Where:

  • \(a\) is the administrative unit (e.g. chiefdom or district)
  • \(t\) is the time period (e.g. “2022-03”)
  • \(o_{a,t}\) is the number of observed facilities in unit \(a\) during time \(t\)
  • \(e_{a,t}\) is the number of expected facilities in unit \(a\) during time \(t\)

Observed facilities are those that submitted a valid (non-missing) value for the indicator of interest during time \(t\).

Expected facilities are those who were expected to report during time \(t\) (see previous section). Remember to consult with the SNT team to decide what rules determine whether a facility is expected to report.

Worked example

PLEASE MAKE SURE THIS EXAMPLE IS USING ACTUAL NUMBERS FOR KAILAHUN DISTRICT IN MARCH 2022

Suppose we are calculating the reporting rate for total confirmed cases for Kailahun District in March 2022.

  • There are 6 health facilities in Kailahun that have ever submitted data on any key malaria indicator
  • All 6 submitted their first report on or before March 2022, so they are assumed to be active and expected to report that month
  • Of these, 4 facilities reported a valid value for conf (total confirmed cases) in March 2022 → 4 are observed reporting
  • The other two do not have a valid value for total confirmed cases (they show NA in the database) for March 2022

The reporting rate is calculated as:

\[ \text{Reporting Rate for Total Confirmed Cases}_{\text{Kailahun}, \text{Mar 2022}} = \frac{4}{6} = 0.67 \]

Weighted reporting rates

For some SNT applications, a weighted reporting rate may be of interest. The weighted reporting rate is an estimate of the proportion of an indicator’s expected total counts in a given admin unit over a given time period that was reported into routine surveillance.

This means that if a non-reporting facility generally reports fewer confirmed cases than average in its admin unit, the weighted reporting rate for confirmed cases is higher than the unweighted reporting rate (fewer cases are missing). Conversely, if a non-reporting facility generally reports relatively many confirmed cases for its admin unit, the weighted reporting rate for confirmed cases is lower than the unweighted reporting rate (more cases are missing).

Calculating weighted reporting rates

PLACEHOLDER EXPLANATION BELOW. WOULD BE GREAT TO INCLUDE THE SIMPLE EXAMPLE FROM BEA’S SLIDE DECK incl the illustration if possible

The monthly weighted reporting rate for each health district was determined as follows. For each calendar month (January through December), health facility weights were calculated by dividing the health facility’s average number of malaria cases reported for that month, across all years of data, by the district sum of the average number of malaria cases reported for that month, across all years of data. For dates in which a health facility is inactive, the average number of malaria cases reported for that month-and-year pair would be 0, and the weights for the health facilities in that district for that date would be calculated as usual. The district monthly weighted reporting rate was then calculated by summing the weights of the active health facilities. This value captures the proportion of expected confirmed malaria cases that are reported at the district level each month by the active health facilities included in the HMIS database.

How do I know whether to use unweighted or weighted reporting rate?

To assess the performance of the routine surveillance system, the unweighted reporting rate is likely to be more appropriate.

To estimate an admin unit’s unreported confirmed cases (as an example indicator), the weighted reporting rate may be the more accurate option, as it will account for the size of the non-reporting facility. If you choose to use weighted reporting rates, best practice is to also calculate the unweighted reporting rates for the same indicator, compare the two outputs, and discuss with the SNT team.

WOULD BE GREAT TO INCLUDE IN THE STEP BY STEP EXAMPLE VISUALIZATIONS. BOTH SEBASTIAN’S LINE PLOT VERSION AND OUSMANE’S HEATMAPS

Consult SNT team

If you think the weighted reporting rate might be the better option, produce reporting rates for using both methods and present to the SNT team. Discuss with the SNT team to understand how, where, when, and why the two reporting rates are different. Together with the SNT team, discuss which to use for downstream analysis.

Step-by-Step

Now that we’ve defined how reporting rates are constructed—by identifying active facilities and calculating observed reporting—we move into the step-by-step process for implementing this in code using example DHIS2 data from Sierra Leone. In this section, we walk through the steps for calculating and visualising monthly reporting rates. Each step is designed to guide you through the process. Follow the notes in the code, especially where edits are required.

To skip the step-by-step explanation, jump to the full code at the end of this page.

Objectives
  • Calculate monthly reporting rates
  • Visualise reporting rate over time, by indicator

Step 1: Import relevant packages and pre-processed routine data

In this step, we load the necessary packages to run this section, as well as the routine DHIS2 dataset that was initially processed in the DHIS2 Data Preprocessing section of this code library.

  • R
  • Python
Output

To adapt the code:

# | message: false
# | echo: true
# | eval: false
# | code-fold: false

# Import packages
import pandas as pd
import numpy as np
from pyhere import here
import matplotlib.pyplot as plt
import seaborn as sns
from collections import OrderedDict
import matplotlib.patches as mpatches

# import dhis2 data data
dhis2_df = pd.read_parquet(
    here("english/data_r/routine_cases", "dhis2_processed_data_python.parquet")
)

# Inspect
dhis2_df.head(10).style
  Year Month YM adm0 adm0_uid adm1 adm1_uid adm2 adm2_uid adm3 adm3_uid hf hf_uid allout allout_ov5 allout_u5 conf conf_5_14 conf_com conf_com_5_14 conf_com_ov15 conf_com_u5 conf_hf conf_hf_5_14 conf_hf_ov15 conf_hf_u5 conf_ov15 conf_u5 maladm maladm_5_14 maladm_ov15 maladm_u5 maldth maldth_10_14 maldth_1_59m maldth_5_14 maldth_5_9 maldth_fem_ov15 maldth_mal_ov15 maldth_ov15 maldth_u5 maltreat maltreat_5_14 maltreat_com maltreat_com_5_14 maltreat_com_ov15 maltreat_com_u5 maltreat_hf maltreat_hf_5_14 maltreat_hf_ov15 maltreat_hf_u5 maltreat_ov15 maltreat_ov24_5_14_com maltreat_ov24_5_14_hf maltreat_ov24_com maltreat_ov24_hf maltreat_ov24_ov15_com maltreat_ov24_ov15_hf maltreat_ov24_total maltreat_ov24_u5_com maltreat_ov24_u5_hf maltreat_u24_5_14_com maltreat_u24_5_14_hf maltreat_u24_com maltreat_u24_hf maltreat_u24_ov15_com maltreat_u24_ov15_hf maltreat_u24_total maltreat_u24_u5_com maltreat_u24_u5_hf maltreat_u5 pres pres_5_14 pres_com pres_com_5_14 pres_com_ov15 pres_com_u5 pres_hf pres_hf_5_14 pres_hf_ov15 pres_hf_u5 pres_ov15 pres_u5 susp susp_5_14 susp_com_5_14 susp_com_ov15 susp_com_u5 susp_hf_5_14 susp_hf_ov15 susp_hf_u5 susp_ov15 susp_u5 test test_5_14 test_com test_com_5_14 test_com_ov15 test_com_u5 test_hf test_hf_5_14 test_hf_ov15 test_hf_u5 test_neg_mic_5_14_hf test_neg_mic_ov15_hf test_neg_mic_u5_hf test_neg_rdt_5_14_com test_neg_rdt_5_14_hf test_neg_rdt_ov15_com test_neg_rdt_ov15_hf test_neg_rdt_u5_com test_neg_rdt_u5_hf test_ov15 test_pos_mic_5_14_hf test_pos_mic_ov15_hf test_pos_mic_u5_hf test_pos_rdt_5_14_com test_pos_rdt_5_14_hf test_pos_rdt_ov15_com test_pos_rdt_ov15_hf test_pos_rdt_u5_com test_pos_rdt_u5_hf test_u5
0 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Aethel CHP HF_00001 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
1 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Agape Way CHP HF_00002 260.000000 130.000000 130.000000 280.000000 104.000000 nan nan nan nan 280.000000 104.000000 40.000000 136.000000 40.000000 136.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan 297.000000 110.000000 17.000000 6.000000 7.000000 4.000000 280.000000 104.000000 40.000000 136.000000 47.000000 nan nan nan nan nan nan nan nan nan 6.000000 104.000000 17.000000 280.000000 7.000000 40.000000 297.000000 4.000000 136.000000 140.000000 0.000000 0.000000 nan nan nan nan 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 464.000000 134.000000 nan nan nan 134.000000 60.000000 270.000000 60.000000 270.000000 464.000000 134.000000 nan nan nan nan 464.000000 134.000000 60.000000 270.000000 nan nan nan nan 30.000000 nan 20.000000 nan 134.000000 60.000000 nan nan nan nan 104.000000 nan 40.000000 nan 136.000000 270.000000
2 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Anglican Diocese Clinic HF_00003 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
3 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Batiama Layout MCHP HF_00004 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
4 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Bo Government Hospital HF_00005 784.000000 309.000000 475.000000 nan nan nan nan nan nan nan nan nan nan nan nan 19.000000 11.000000 1.000000 7.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan 182.000000 nan nan nan nan nan 14.000000 168.000000 14.000000 168.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
5 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Bo School Bay CHP HF_00006 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
6 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Breakthrough MCHP HF_00007 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
7 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Brima Town CHP HF_00008 241.000000 139.000000 102.000000 95.000000 6.000000 nan nan nan nan 95.000000 6.000000 29.000000 60.000000 29.000000 60.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan 95.000000 6.000000 nan nan nan nan 95.000000 6.000000 29.000000 60.000000 29.000000 nan 1.000000 nan 34.000000 nan 12.000000 34.000000 nan 21.000000 nan 5.000000 nan 61.000000 nan 17.000000 61.000000 nan 39.000000 60.000000 0.000000 0.000000 nan nan nan nan 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 106.000000 7.000000 nan nan nan 7.000000 30.000000 69.000000 30.000000 69.000000 106.000000 7.000000 nan nan nan nan 106.000000 7.000000 30.000000 69.000000 nan nan nan nan 1.000000 nan 1.000000 nan 9.000000 30.000000 nan nan nan nan 6.000000 nan 29.000000 nan 60.000000 69.000000
8 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 EDC Unit CHP HF_00009 583.000000 217.000000 366.000000 316.000000 17.000000 nan nan nan nan 316.000000 17.000000 102.000000 197.000000 102.000000 197.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan 316.000000 17.000000 nan nan nan nan 316.000000 17.000000 102.000000 197.000000 102.000000 nan nan nan nan nan nan nan nan nan nan 17.000000 nan 316.000000 nan 102.000000 316.000000 nan 197.000000 197.000000 0.000000 0.000000 nan nan nan nan 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 528.000000 27.000000 nan nan nan 27.000000 190.000000 311.000000 190.000000 311.000000 528.000000 27.000000 nan nan nan nan 528.000000 27.000000 190.000000 311.000000 nan nan nan nan 10.000000 nan 88.000000 nan 114.000000 190.000000 nan nan nan nan 17.000000 nan 102.000000 nan 197.000000 311.000000
9 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Favour MCHP HF_00010 159.000000 87.000000 72.000000 152.000000 25.000000 nan nan nan nan 152.000000 25.000000 62.000000 65.000000 62.000000 65.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan 152.000000 25.000000 nan nan nan nan 152.000000 25.000000 62.000000 65.000000 62.000000 nan nan nan nan nan nan nan nan nan nan 25.000000 nan 152.000000 nan 62.000000 152.000000 nan 65.000000 65.000000 0.000000 0.000000 nan nan nan nan 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 174.000000 26.000000 nan nan nan 26.000000 69.000000 79.000000 69.000000 79.000000 174.000000 26.000000 nan nan nan nan 174.000000 26.000000 69.000000 79.000000 nan nan nan nan 1.000000 nan 7.000000 nan 14.000000 69.000000 nan nan nan nan 25.000000 nan 62.000000 nan 65.000000 79.000000
Output
# | fig-width: 10
# | fig-height: 9
# | fig-align: center
# | echo: false
# | eval: true
# | warning: false

# Import packages
import pandas as pd
import numpy as np
from pyhere import here
import matplotlib.pyplot as plt
import seaborn as sns
from collections import OrderedDict
import matplotlib.patches as mpatches

# import dhis2 data data
dhis2_df = pd.read_parquet(
    here("english/data_r/routine_cases", "dhis2_processed_data_python.parquet")
)


# Inspect
dhis2_df.head(10).style
  Year Month YM adm0 adm0_uid adm1 adm1_uid adm2 adm2_uid adm3 adm3_uid hf hf_uid allout allout_ov5 allout_u5 conf conf_5_14 conf_com conf_com_5_14 conf_com_ov15 conf_com_u5 conf_hf conf_hf_5_14 conf_hf_ov15 conf_hf_u5 conf_ov15 conf_u5 maladm maladm_5_14 maladm_ov15 maladm_u5 maldth maldth_10_14 maldth_1_59m maldth_5_14 maldth_5_9 maldth_fem_ov15 maldth_mal_ov15 maldth_ov15 maldth_u5 maltreat maltreat_5_14 maltreat_com maltreat_com_5_14 maltreat_com_ov15 maltreat_com_u5 maltreat_hf maltreat_hf_5_14 maltreat_hf_ov15 maltreat_hf_u5 maltreat_ov15 maltreat_ov24_5_14_com maltreat_ov24_5_14_hf maltreat_ov24_com maltreat_ov24_hf maltreat_ov24_ov15_com maltreat_ov24_ov15_hf maltreat_ov24_total maltreat_ov24_u5_com maltreat_ov24_u5_hf maltreat_u24_5_14_com maltreat_u24_5_14_hf maltreat_u24_com maltreat_u24_hf maltreat_u24_ov15_com maltreat_u24_ov15_hf maltreat_u24_total maltreat_u24_u5_com maltreat_u24_u5_hf maltreat_u5 pres pres_5_14 pres_com pres_com_5_14 pres_com_ov15 pres_com_u5 pres_hf pres_hf_5_14 pres_hf_ov15 pres_hf_u5 pres_ov15 pres_u5 susp susp_5_14 susp_com_5_14 susp_com_ov15 susp_com_u5 susp_hf_5_14 susp_hf_ov15 susp_hf_u5 susp_ov15 susp_u5 test test_5_14 test_com test_com_5_14 test_com_ov15 test_com_u5 test_hf test_hf_5_14 test_hf_ov15 test_hf_u5 test_neg_mic_5_14_hf test_neg_mic_ov15_hf test_neg_mic_u5_hf test_neg_rdt_5_14_com test_neg_rdt_5_14_hf test_neg_rdt_ov15_com test_neg_rdt_ov15_hf test_neg_rdt_u5_com test_neg_rdt_u5_hf test_ov15 test_pos_mic_5_14_hf test_pos_mic_ov15_hf test_pos_mic_u5_hf test_pos_rdt_5_14_com test_pos_rdt_5_14_hf test_pos_rdt_ov15_com test_pos_rdt_ov15_hf test_pos_rdt_u5_com test_pos_rdt_u5_hf test_u5
0 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Aethel CHP HF_00001 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
1 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Agape Way CHP HF_00002 260.000000 130.000000 130.000000 280.000000 104.000000 nan nan nan nan 280.000000 104.000000 40.000000 136.000000 40.000000 136.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan 297.000000 110.000000 17.000000 6.000000 7.000000 4.000000 280.000000 104.000000 40.000000 136.000000 47.000000 nan nan nan nan nan nan nan nan nan 6.000000 104.000000 17.000000 280.000000 7.000000 40.000000 297.000000 4.000000 136.000000 140.000000 0.000000 0.000000 nan nan nan nan 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 464.000000 134.000000 nan nan nan 134.000000 60.000000 270.000000 60.000000 270.000000 464.000000 134.000000 nan nan nan nan 464.000000 134.000000 60.000000 270.000000 nan nan nan nan 30.000000 nan 20.000000 nan 134.000000 60.000000 nan nan nan nan 104.000000 nan 40.000000 nan 136.000000 270.000000
2 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Anglican Diocese Clinic HF_00003 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
3 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Batiama Layout MCHP HF_00004 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
4 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Bo Government Hospital HF_00005 784.000000 309.000000 475.000000 nan nan nan nan nan nan nan nan nan nan nan nan 19.000000 11.000000 1.000000 7.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan 182.000000 nan nan nan nan nan 14.000000 168.000000 14.000000 168.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
5 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Bo School Bay CHP HF_00006 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
6 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Breakthrough MCHP HF_00007 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan
7 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Brima Town CHP HF_00008 241.000000 139.000000 102.000000 95.000000 6.000000 nan nan nan nan 95.000000 6.000000 29.000000 60.000000 29.000000 60.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan 95.000000 6.000000 nan nan nan nan 95.000000 6.000000 29.000000 60.000000 29.000000 nan 1.000000 nan 34.000000 nan 12.000000 34.000000 nan 21.000000 nan 5.000000 nan 61.000000 nan 17.000000 61.000000 nan 39.000000 60.000000 0.000000 0.000000 nan nan nan nan 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 106.000000 7.000000 nan nan nan 7.000000 30.000000 69.000000 30.000000 69.000000 106.000000 7.000000 nan nan nan nan 106.000000 7.000000 30.000000 69.000000 nan nan nan nan 1.000000 nan 1.000000 nan 9.000000 30.000000 nan nan nan nan 6.000000 nan 29.000000 nan 60.000000 69.000000
8 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 EDC Unit CHP HF_00009 583.000000 217.000000 366.000000 316.000000 17.000000 nan nan nan nan 316.000000 17.000000 102.000000 197.000000 102.000000 197.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan 316.000000 17.000000 nan nan nan nan 316.000000 17.000000 102.000000 197.000000 102.000000 nan nan nan nan nan nan nan nan nan nan 17.000000 nan 316.000000 nan 102.000000 316.000000 nan 197.000000 197.000000 0.000000 0.000000 nan nan nan nan 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 528.000000 27.000000 nan nan nan 27.000000 190.000000 311.000000 190.000000 311.000000 528.000000 27.000000 nan nan nan nan 528.000000 27.000000 190.000000 311.000000 nan nan nan nan 10.000000 nan 88.000000 nan 114.000000 190.000000 nan nan nan nan 17.000000 nan 102.000000 nan 197.000000 311.000000
9 2015 1 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 Favour MCHP HF_00010 159.000000 87.000000 72.000000 152.000000 25.000000 nan nan nan nan 152.000000 25.000000 62.000000 65.000000 62.000000 65.000000 nan nan nan nan nan nan nan nan nan nan nan nan nan 152.000000 25.000000 nan nan nan nan 152.000000 25.000000 62.000000 65.000000 62.000000 nan nan nan nan nan nan nan nan nan nan 25.000000 nan 152.000000 nan 62.000000 152.000000 nan 65.000000 65.000000 0.000000 0.000000 nan nan nan nan 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 174.000000 26.000000 nan nan nan 26.000000 69.000000 79.000000 69.000000 79.000000 174.000000 26.000000 nan nan nan nan 174.000000 26.000000 69.000000 79.000000 nan nan nan nan 1.000000 nan 7.000000 nan 14.000000 69.000000 nan nan nan nan 25.000000 nan 62.000000 nan 65.000000 79.000000

To adapt the code: Edit the filepath on line 11.

Step 2: Import your denominator dataframe - dfden

Here we import the reporting rate denominator dataframe we built in section link to active status section.

  • R
  • Python
Output

To adapt the code:

# import denominator data
dfden = pd.read_csv(here('english/data_r/routine_cases', 'dfden.csv'))

# Inspect
dfden.head(10).style
Output
  Year YM adm0 adm0_uid adm1 adm1_uid adm2 adm2_uid adm3 adm3_uid denominator
0 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo City Council adm2_00001 Bo City adm3_00001 21
1 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo District Council adm2_00002 Badjia Chiefdom adm3_00002 2
2 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo District Council adm2_00002 Bagbwe Chiefdom adm3_00003 6
3 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo District Council adm2_00002 Baoma Chiefdom adm3_00004 16
4 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo District Council adm2_00002 Bargbo Chiefdom adm3_00005 8
5 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo District Council adm2_00002 Bongor Chiefdom adm3_00006 4
6 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo District Council adm2_00002 Bumpe Ngao Chiefdom adm3_00007 13
7 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo District Council adm2_00002 Gbo Chiefdom adm3_00008 2
8 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo District Council adm2_00002 Jaiama Chiefdom adm3_00009 3
9 2015 2015-01 Sierra Leone adm0_00001 Bo District adm1_00001 Bo District Council adm2_00002 Kakua Chiefdom adm3_00010 8

To adapt the code: Edit the filepath on line 2.

Step 3: Define function to calculate reporting rate

Now we define a function to calculate the monthly reporting rate, for a given indicator, at a given admin unit level. The function performs the following steps:

  • Count the number of non-NA reports for the indicator of interest (observed reports), aggregated by month and by the admin unit level requested by the user
  • Count the number of expected reports by month and admin uit unit level requested by the user, by aggregating dfden
  • Merge the two datasets (observed reports and expected reports, i.e. reporting rate numerator and denominator)
  • Compute the reporting rate
  • R
  • Python
Output

To adapt the code:

def compute_RR(base_df, dfden, level, indicator):
    cols_group = ['YM', f'adm{level}', f'adm{level}_uid']
    df = base_df.copy()

    # count number of non-null reports for confirmed cases
    df = df.groupby(cols_group)[indicator].count().reset_index()

    # aggregate denominator at this level
    temp = (dfden.groupby(cols_group)['denominator']
            .sum(min_count = 1)
            .reset_index())

    # merge numerator and denominator
    df = df.merge(temp, on = cols_group, how = 'outer', validate = '1:1')

    # compute reporting rate
    df.insert(len(df.columns), f'{indicator}_RR', np.where(df['denominator'] == 0, np.nan, df[indicator].div(df['denominator'])))

    # rename columns and output dataframe
    df = df.rename(columns = {indicator: 'observed', 'denominator': 'expected'})
    df = df[['YM', f'adm{level}', f'adm{level}_uid', 'observed', 'expected', f'{indicator}_RR']]

    return df

To adapt the code: Nothing to adapt here.

Step 4: Calculate reporting rate for a given indicator, at a given admin unit level

  • R
  • Python
Output

To adapt the code:

# define indicator and admin unit level
level = 3
indicator = 'conf'

# calculate reporting rate
df = compute_RR(dhis2_df, dfden, level, indicator)

# Save
df.to_csv(here('english/data_r/routine_cases', f'Monthly_{indicator}_RR_adm{level}.csv'), index = None)

# Inspect results
df.head(10).style
Output
  YM adm3 adm3_uid observed expected conf_RR
0 2015-01 Badjia Chiefdom adm3_00002 2 2 1.000000
1 2015-01 Bagbwe Chiefdom adm3_00003 6 6 1.000000
2 2015-01 Bake-Loko Chiefdom adm3_00097 2 3 0.666667
3 2015-01 Baoma Chiefdom adm3_00004 13 16 0.812500
4 2015-01 Barawa Wollay Chiefdom adm3_00030 1 1 1.000000
5 2015-01 Bargbo Chiefdom adm3_00005 8 8 1.000000
6 2015-01 Barri Chiefdom adm3_00110 8 9 0.888889
7 2015-01 Bendu-Cha Chiefdom adm3_00018 3 3 1.000000
8 2015-01 Bo City adm3_00001 20 21 0.952381
9 2015-01 Bongor Chiefdom adm3_00006 4 4 1.000000

To adapt the code: Set the parameters on lines 2 and 3, and adapt the filepath on line 9.

Step 5: Visualise indicator-specific reporting rates

VT: Do we want additional visualisations here, line plots over time maybe, maps? To discuss with team

Step 5.1 Multiple indicators, nationally

  • R
  • Python
Output

To adapt the code:

Show the code
# Make a heatmap of monthly reporting rate by variable
indicators =  ['allout', 'test', 'conf', 'pres', 'maltreat']
level = 0

df = pd.DataFrame(columns = ['YM'])
for i in indicators:
    t = compute_RR(dhis2_df, dfden, level, i).drop([f'adm{level}', f'adm{level}_uid', 'observed', 'expected'], axis = 1)
    # make RR a percentage for visual
    t[f'{i}_RR'] = t[f'{i}_RR']*100
    df = df.merge(t, on = 'YM', how = 'outer', validate = '1:1')

# rearrange for heatmap
df = df.set_index('YM').T

fig, ax = plt.subplots(figsize = (15, 6))
cbar_ax = fig.add_axes([.91, .2, .02, .6])

sns.heatmap(ax = ax
            , data = df
            , cmap = 'viridis'
            , cbar_ax = cbar_ax
            , vmin = 0
            , vmax = 100
            , cbar_kws = {'label': '%'})

ax.set_xlabel('')
ax.set_xticks(ax.get_xticks())
ax.set_xticklabels(ax.get_xticklabels(), rotation = 45, ha = 'right')
ax.set_yticks(ax.get_yticks())
ax.set_yticklabels([l.get_text()[0:-3] for l in ax.get_yticklabels()], rotation = 0, ha = 'right')

ax.set_title('Monthly reporting rate')

fig.savefig(here('english/data_r/routine_cases', f'Monthly_reporting_rate_by_variable_heatmap.png')
            , facecolor = 'white'
            , bbox_inches = 'tight')

plt.show()
Output

To adapt the code:

Step 5.2 Single indicator, by admin unit

  • R
  • Python
Output

To adapt the code:

Show the code
# VT to clean this up after agreeing on import.qmd with team
dftree = dhis2_df[['adm0', 'adm0_uid', 'adm1', 'adm1_uid', 'adm2', 'adm2_uid', 'adm3', 'adm3_uid']].drop_duplicates().reset_index(drop = True)

level = 3
level_yaxis = 1
indicator = 'conf'
fs = 15

df = compute_RR(dhis2_df, dfden, level, indicator)
df[f'{indicator}_RR'] = df[f'{indicator}_RR']*100

# rearrange for heatmap
df = df.pivot(index = [f'adm{level}'], columns = 'YM', values = f'{indicator}_RR')
t = dftree[[f'adm{level_yaxis}', f'adm{level}']].drop_duplicates()
df = df.merge(t, on = f'adm{level}', how = 'left', validate = 'm:1')

df = (df.sort_values(by = [f'adm{level_yaxis}', f'adm{level}'])
      .reset_index(drop= True)
      .set_index([f'adm{level_yaxis}', f'adm{level}']))


fig, ax = plt.subplots(figsize = (30, 15))
cbar_ax = fig.add_axes([.91, .2, .02, .6])

sns.heatmap(ax = ax
            , data = df
            , cmap = 'viridis'
            , cbar_ax = cbar_ax
            , vmin = 0
            , vmax = 100
            , cbar_kws = {'label': '%'})


_ = ax.set_xlabel('')
_ = ax.set_xticks(ax.get_xticks())
_ = ax.set_xticklabels(ax.get_xticklabels(), rotation = 45, ha = 'right')

# trick to label adm unit names nicely on the yaxis
yticklabels = [l.get_text().split('-')[0] for l in ax.get_yticklabels()]
t = pd.DataFrame(yticklabels).reset_index()
t1 = t.groupby(0)['index'].mean().astype(int).reset_index()
t.insert(0, 'pos', t[0].map(dict(zip(t1[0], t1['index']))))
t = t[[0, 'pos', 'index']]
t[0] = np.where(t.pos == t['index'], t[0], '')
test = t[0].to_list()

_ = ax.set_yticks(ax.get_yticks())
_ = ax.set_yticklabels(test, size = fs)
_ = ax.set_ylabel(f'adm{level}', size = fs)

ylabel_mapping = OrderedDict()

for adm1, adm3_uid in df.index:
    ylabel_mapping.setdefault(adm1, [])
    ylabel_mapping[adm1].append(adm3_uid)

hline = []
new_ylabels = []

for adm1, adm3_list in ylabel_mapping.items():
    adm3_list[0] = "{} - {}".format(adm1, adm3_list[0])
    new_ylabels.extend(adm3_list)

    if hline:
        hline.append(len(adm3_list) + hline[-1])
    else:
        hline.append(len(adm3_list))

ax.hlines(hline, xmin = -10, xmax = 0, color = "grey", linewidth = 2, clip_on = False)

# color NaN values
colour = 'grey'
ax.set_facecolor(colour);
handle = [mpatches.Patch(color = colour, label = 'No data')]
ax.legend(handles = handle
          , fontsize = fs
      , bbox_to_anchor = (1, 0)
      , loc = 'lower left')


ax.set_title(f'Monthly {indicator} reporting rate by adm{level}', size = fs);

plt.show()
Output

To adapt the code:

Full code

Find the full code script for calculating reporting rates below.

  • R
  • Python
 

©2025 Applied Health Analytics for Delivery and Innovation. All rights reserved