# Setup
import math as m
The following sections are used to calculate the number of GP Practices required, and the number of potential participants to be approached at each GP Practice.
n_pts = 130 # Total number of participants in SAFER Wearables
n_AF_pts = n_pts/2 # Number of AF participants
n_nonAF_pts = n_pts/2 # Number of non-AF participants
n_safer_pts_per_prac = 350 # Number of SAFER Programme participants per practice
AF_rate = 0.03 # Proportion of participants diagnosed with AF in SAFER Programme
n_suggested_per_practice = 45 # number of potential participants suggested to each practice
prop_contacted = 0.8 # proportion of SAFER participants who are suitable for the SAFER Wearables study who will be contacted by their practice (remaidner won't be due to changing practice, or not meeting the eligibility criteria)
participation_rate = 0.5 # proportion of invitees who will participate
# - SAFER participants
n_safer_AF_pts_per_prac = m.floor(AF_rate*n_safer_pts_per_prac)
n_safer_nonAF_pts_per_prac = m.floor((1-AF_rate)*n_safer_pts_per_prac)
# - Suggested possible participants
n_safer_AF_pts_per_prac_suggested = n_safer_AF_pts_per_prac
n_safer_nonAF_pts_per_prac_suggested = n_suggested_per_practice - n_safer_AF_pts_per_prac_suggested
n_safer_pts_per_prac_suggested = n_safer_AF_pts_per_prac_suggested + n_safer_nonAF_pts_per_prac_suggested
print("Number suggested potential participants per practice: %d (%d AF, %d non-AF)" % (n_safer_pts_per_prac_suggested, n_safer_AF_pts_per_prac_suggested, n_safer_nonAF_pts_per_prac_suggested))
# - invitations
n_AF_pts_invited_per_prac = m.floor(n_safer_AF_pts_per_prac_suggested*prop_contacted)
n_nonAF_pts_invited_per_prac = m.floor(n_safer_nonAF_pts_per_prac_suggested*prop_contacted)
n_invites_per_prac = n_nonAF_pts_invited_per_prac + n_AF_pts_invited_per_prac
print("Number invitations per practice: %d (%d AF, %d non-AF)" % (n_invites_per_prac, n_AF_pts_invited_per_prac, n_nonAF_pts_invited_per_prac))
# - participants
n_AF_pts_per_prac = m.floor(n_AF_pts_invited_per_prac*participation_rate)
n_nonAF_pts_per_prac = m.floor(n_nonAF_pts_invited_per_prac*participation_rate)
n_pts_per_prac = n_AF_pts_per_prac + n_nonAF_pts_per_prac
print("Expected number SAFER Wearables participants per practice: %d (%d AF, %d non-AF)" % (n_pts_per_prac, n_AF_pts_per_prac, n_nonAF_pts_per_prac))
Number suggested potential participants per practice: 45 (10 AF, 35 non-AF) Number invitations per practice: 36 (8 AF, 28 non-AF) Expected number SAFER Wearables participants per practice: 18 (4 AF, 14 non-AF)
no_practices_for_AF = m.ceil(n_AF_pts/n_AF_pts_per_prac) # no. practices required to reach the required number of AF participants
no_practices_for_nonAF = m.ceil(n_nonAF_pts/n_nonAF_pts_per_prac) # no. practices required to reach the required number of non-AF participants
no_practices = max([no_practices_for_AF, no_practices_for_nonAF])
print("No. GP Practices required: %d (%d for AF, and %d for non-AF)" % (no_practices, no_practices_for_AF, no_practices_for_nonAF))
No. GP Practices required: 17 (17 for AF, and 5 for non-AF)
Based on this approach, 17 practices are required, and each practice needs to have 10 potential AF participants.
no_practices_to_participate = 19 # no. practices which will participate
ave_no_potential_af_participants_per_practice = n_AF_pts/(no_practices_to_participate*prop_contacted*participation_rate)
print("Ave no. of potential AF participants required per practice (if %d practices participate): %.1f" % (no_practices_to_participate, ave_no_potential_af_participants_per_practice))
Ave no. of potential AF participants required per practice (if 19 practices participate): 8.6
Based on this approach, each practice should have (on average) 8.6 potential AF participants. Therefore, you could for instance approach practices with at least 6 potential AF participants, assuming that on average this will provide an average of at least 8.6 potential AF participants per practice.
If all the non-AF participants are enrolled early in the study (likely as they are expected to be enrolled in the first 5 Practices), then it might be worth exploring only recruiting AF participants from that point onwards, which may allow a greater number of practices to participate.
# actual costs of devices
act_cost_wrist_ppg_device = 148 # actual cost of wrist-worn PPG device (GBP)
act_cost_wrist_ecg_device = 130 # actual cost of wrist-worn ECG device (GBP)
act_cost_mobile_phone = 100 # actual cost of mobile phone (GBP)
# NB: cost of reference ECG chest-patch device not included, as this has been purchased already
# permitted costs of devices
perm_cost_wrist_ppg_device = 151 # actual cost of wrist-worn PPG device (GBP)
perm_cost_wrist_ecg_device = 100 # actual cost of wrist-worn ECG device (GBP)
perm_cost_mobile_phone = 100 # actual cost of mobile phone (GBP)
# No. device kits
no_kits_with_phone = 20
no_kits_without_phone = 0
no_of_each_wearable = no_kits_with_phone + no_kits_without_phone # no. of each wearable to be purchased
no_mobile_phones = no_kits_with_phone
This calculates any overspend in purchasing the devices relative to the funding available.
# calculate over spend
wrist_ppg_device_overspend = max(0, no_of_each_wearable*(act_cost_wrist_ppg_device-perm_cost_wrist_ppg_device))
wrist_ecg_device_overspend = max(0, no_of_each_wearable*(act_cost_wrist_ecg_device-perm_cost_wrist_ecg_device))
mobile_phone_overspend = max(0, no_mobile_phones*(act_cost_mobile_phone-perm_cost_mobile_phone))
total_overspend = wrist_ppg_device_overspend + wrist_ecg_device_overspend + mobile_phone_overspend
print("Total overspend: \xA3%.2f" % (total_overspend)) # NB: \xA3 is the pound-sign
print(" - PPG devices: \xA3%.2f" % (wrist_ppg_device_overspend))
print(" - ECG devices: \xA3%.2f" % (wrist_ecg_device_overspend))
print(" - mobile phones: \xA3%.2f" % (mobile_phone_overspend))