Cardiovascular disease (CVD) is the leading cause of death worldwidewith Hypertension, specifically, affecting over 1.1 billion peopleannually. The goal of the package is to provide a comprehensive toolboxfor analyzing blood pressure (BP) data using a variety of statisticalmetrics and visualizations to bring more clarity to CVD.
You can install the released version of bp fromCRAN with:
install.packages("bp")You can install the development version fromGitHub with:
# install.packages("devtools")devtools::install_github("johnschwenck/bp")For installation with vignettes:
devtools::install_github("johnschwenck/bp",build_vignettes =TRUE)The package includes six sample data sets:
bp_hypnos: The data set contains ABPM measurements fromn= 5subjects with type 2diabetes and obstructive sleep apnea. Therecordings are taken every hour during the 24-hour period for each ofthe two visits that are 3 months apart. Additional information includessleep/wake indicator for each recording inferred from a wrist-wornactigraphy device. The aim of original study (Rooney et al.2021) is todetermine the effect of positive airway pressure treatment of sleepapnea on glycemic control and blood pressure of patients with type2diabetes. The included data is a subset of the full data described inRooneyetal.(2021)bp_jhs: The data set consists of HBPM measurements froma2019single-subject pilot study during an endured aerobic (endurance)exercise: cycling 5,775 miles from New York City, New York to Seward,Alaska over the course of 95 days. Data was collected using an OmronEvolv wireless blood pressure monitor twice per day (in the morning uponwaking up and in the evening before bed). The aim of original study(Schwenck 2019) is to assess blood pressure variability across differentenvironments through time.bp_ghana: The data set contains HBPM measurements on n=757subjects across 32 community health centers from cluster-randomizedtrial in Ghana: 389 subjects are in the health insurance coverage (HIC)group and 368 subjects are in another group consisting of a combinationof HIC with a nurse-led task-shifting strategy for hypertension control(TASSH)(this group is denoted TASSH + HIC). Baseline blood pressuremeasurements were collected, with 85% of subjects having available 12month follow-up measurements. The aim of original study (Ogedegbe etal.2018) is to assess the comparative effectiveness of HIC alone versusthe combination of TASSH + HIC on reducing systolic blood pressure amongpatients with uncontrolled hypertension in Ghana.bp_preg: The data set contains HBPM measurements fromn= 209women, each of whom were recorded every 30 minutes during thePregnancy Day Assessment Clinic (PDAC) observation window for up to amaximum of 240 minutes (i.e. a maximum of 8 total readings per subjectper observation window) in addition to an initial “booking” readingbefore the PDAC assessment. The aim of the original study (McCarthy etal.2015) is to investigate the pregnancy-induced hypertension andpre-eclampsia prediction to determine whether the blood pressureassessment of the first observation window of 1 hour (60 minutes) issufficiently accurate relative to the standard 4 hour (240 minute)window.bp_children: The data set contains HBPM measurements onn= 1,283 children from Bristol, UK. Three blood pressure readings pervisit were collected over the course of two observation periods (at ages9 and 11). Additionally, information on their physical activity has beencollected. The aim of original study (Solomon-Moore et al.2020) is toexamine how sedentary behavior affect children progressing throughprimary school, and to understand the relationship between elevatedblood pressure in children and its impact on the development ofcardiovascular disease into adulthood.bp_rats: The data set contains AP measurements sampledat 100 Hz of the SS (n1= 9) and SS.13 (n2= 6) genetic strains of Dahlrats. Each mouse was administered either a low sodium or a high sodiumdiet. The aim of original study (Bugenhagen et al.2010) is toinvestigate the connection between the dysfunction of the baroreflexcontrol system in Dahl rats and salt-sensitive HypertensionThebp package is designed to allow the user toinitialize a processed dataframe through theprocess_datafunction by specifying any combination of the following variablespresent in the user-supplied data set (with the minimum requirement thatSBP andDBP are included). The package willthen utilize the processed dataframe to calculate various metrics frommedical and statistical literature and provide visualizations. Perhapsthe most useful user-friendly feature of the package is the ability togenerate a visualization report to discern relationships and assessblood pressure stage progression among subjects.
The package has the ability to make use of the followingphysiological variables (expressed as integers):
SBP) measured in mmHgDBP) measured in mmHgAP) (if applicable)HR) measured in bpmPP) measured in mmHg which iscalculated as SBP - DBPMAP) measured in mmHgRPP) which is calculated as SBPmultiplied by resting HRThere are also processing functionality for arterial pressure (AP)data which include the following inputs: * A column corresponding to theAP data (AP) * Time elapsed (time_elap)
Furthermore, there are a suite of processing capabilities within theprocess_data function such as:
DATE_TIME: A column coreresponding to a date/timeobject (typicallyas.POSIXct format) such as12/1/2020 13:42:07ID: Identification of individualsVISIT: The visit of each individual, if more than one(integer)WAKE: A binary indicator where 1 denotes awake and 0denotes asleep (binary 1 or 0)GROUP: An arbitrary column that can be used to groupdata according to this category (such as gender or age)EOD: Denotes the end-of-day which can be adjusted forsituations where individuals may take readings after midnightDATA_SCREEN: A screening proceedure to remove outliersbased on upper and lower limits of BP readingsAGG: An aggregation option to average consecutivereadings taken within quick successionCHRON_ORDER: Adjust the chronology of the readings(chronological vs reverse-chronological)TZ: Time zone adjustmentAfter all available variables are identified and processed, theresulting processed dataframe is used for all other functions.
Unique to thebp package is the ability to createadditional columns that might not originally be present in the supplieddata set. At current, the following additional columns will becreated:
TIME_OF_DAY - Corresponds to the Time of Day (Morning,Afternoon, Evening, or Night) based onDATE_TIMEcolumnDAY_OF_WEEK - Corresponds to the Day of the week: auseful column for table visuals. Based onDATE_TIMEcolumnSBP_CATEGORY - Systolic Blood Pressure Stages (Low,Normal, Elevated, Stage 1, Stage 2, Crisis) as defined by the AmericanHeart Association (AHA)DBP_CATEGORY - Diastolic Blood Pressure Stages (Low,Normal, Elevated, Stage 1, Stage 2, Crisis) as defined by the AmericanHeart Association (AHA)BP_CLASS - A two-to-one mapping of SBP and DBP readingsthat expands on the original AHA categories using a more recentadaptation by (Lee et al 2018)See examples below for further details.
The package will then utilize the above variables to calculatevarious metrics from medical and statistical literature in order toquantify and classify the variability of the readings into theirrespective categories of hypertension (normal, elevated, orhypertensive).
The following metrics are currently offered through thebp package:
| Function | Metric Name | Source |
|---|---|---|
bp_arv | Average Real Variability | Mena etal (2005) |
bp_center | Mean and Median | Amaro Lijarcioet al (2006) |
bp_cv | Coefficient of Variation | Munter et al(2011) |
bp_mag | Blood Pressure Magnitude (peak and trough) | Munter et al(2011) |
bp_range | Blood Pressure Range | Levitan et al(2013) |
bp_sv | Successive Variation | Munter et al(2011) |
bp_sleep_metrics | Blood Pressure Sleep Metrics | (Multiple - see documentation) |
bp_stages | Blood Pressure Stages Classification | American Heart Association |
bp_stats | Aggregation of statistical summaries | N/A |
dip_calc | Nocturnal Dipping % and Classification | Okhubo etal (1997) |
The following visualization functions are currently offered throughthebp package:
| Function | Visualization Type |
|---|---|
bp_scatter | Scatter plot of BP stages |
bp_ts_plots | Time series plots |
bp_hist | Histograms of BP stages |
dip_class_plot | Dipping % category plot |
bp_report | Exportable report of BP summary |
There are two main steps involved with thebp package:The data processing step and the functionality / analysis step.
process_data function#devtools::install_github("johnschwenck/bp")library(bp)## Load bp_hypnosdata(bp_hypnos)## Process bp_hypnoshypnos_proc<-process_data(bp_hypnos,sbp ='syst',dbp ='diast',date_time ='date.time',hr ='hr',pp ='PP',map ='MaP',rpp ='Rpp',id ='id',visit ='Visit',wake ='wake')#> 2 values that exceeded the DUL or DLL thresholds were coerced to NA.NOTE: theprocess_data function isinsensitive to capitalization of the supplied data column names. Forthis example, even though the original column name “SYST” exists in thebp_hypnos, “syst” is still an acceptable name to be givento the function as shown. For emphasis, all of the above column nameswere intentionally entered using the wrong capitalization.
SBP andDBP must be specified for any otherfunctions to work properly.
hypnos_proc, we can nowcalculate various metrics. Now that the included rawbp_hypnos dataset has been processed intohypnos_proc, we can now instead rely on this new dataframeto calculate various metrics and visualizations. The calculation of thenocturnal dipping classification is shown below, using a subset of onlytwo of the subjects for comparison (subjects 70417 and 70435):dip_calc(hypnos_proc,subj =c(70417,70435))#> [[1]]#> # A tibble: 8 × 6#> # Groups: ID, VISIT [4]#> ID VISIT WAKE avg_SBP avg_DBP N#> <fct> <fct> <fct> <dbl> <dbl> <int>#> 1 70417 1 0 123. 60.5 10#> 2 70417 1 1 128 66.6 20#> 3 70417 2 0 136. 60.5 8#> 4 70417 2 1 136. 65.6 17#> 5 70435 1 0 106. 63 6#> 6 70435 1 1 129. 82.1 23#> 7 70435 2 0 136. 79.2 9#> 8 70435 2 1 123. 72.5 20#>#> [[2]]#> # A tibble: 4 × 6#> # Groups: ID [2]#> ID VISIT dip_sys class_sys dip_dias class_dias#> <fct> <fct> <dbl> <chr> <dbl> <chr>#> 1 70417 1 0.0359 non-dipper 0.0916 non-dipper#> 2 70417 2 -0.00450 reverse 0.0784 non-dipper#> 3 70435 1 0.179 dipper 0.233 extreme#> 4 70435 2 -0.104 reverse -0.0927 reverseIn terms of statistical metrics, thebp_stats functionaggregates many of the variability and center metrics into one tablewhich makes comparing the different measures to one another veryconvenient. Let’s suppose for this example that we wanted to furtheranalyze these two subjects by theirBP_CLASS: we wouldincludeadd_groups = "BP_CLASS" as an additional argument(note that capitalization does not matter).
bp_stats(hypnos_proc,subj =c(70417,70435),add_groups ="bp_class",bp_type ="both")#> # A tibble: 35 × 27#> # Groups: ID, VISIT, WAKE [8]#> ID N VISIT WAKE BP_CLASS SBP_mean DBP_mean SBP_med DBP_med SD_SBP#> <fct> <int> <fct> <fct> <ord> <dbl> <dbl> <dbl> <dbl> <dbl>#> 1 70417 5 1 0 Normal 114. 54.6 114 56 3.51#> 2 70417 3 1 0 Elevated 125. 60.3 125 64 1.53#> 3 70417 2 1 0 ISH - S2 143 75.5 143 75.5 2.83#> 4 70417 3 1 1 Normal 116. 67 118 68 4.04#> 5 70417 9 1 1 Elevated 125. 65.2 124 66 2.54#> 6 70417 6 1 1 ISH - S1 134. 67.8 133 67.5 2.42#> 7 70417 2 1 1 ISH - S2 144 68.5 144 68.5 1.41#> 8 70417 3 2 0 Elevated 124 57.7 122 57 4.36#> 9 70417 2 2 0 ISH - S1 133 56.5 133 56.5 1.41#> 10 70417 3 2 0 ISH - S2 151 66 151 65 0#> # ℹ 25 more rows#> # ℹ 17 more variables: SD_DBP <dbl>, ARV_SBP <dbl>, ARV_DBP <dbl>,#> # SV_SBP <dbl>, SV_DBP <dbl>, CV_SBP <dbl>, CV_DBP <dbl>, SBP_max <dbl>,#> # SBP_min <dbl>, DBP_max <dbl>, DBP_min <dbl>, SBP_range <dbl>,#> # DBP_range <dbl>, Peak_SBP <dbl>, Peak_DBP <dbl>, Trough_SBP <dbl>,#> # Trough_DBP <dbl>Here is an example of thebp_scatter function forsubject 70417:
bp_scatter(hypnos_proc,subj =70417)#> Warning in geom_text(aes(x = min(xlim_breaks) + 5, y = low_y_lim[2], label = "Low"), : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.#> Warning in geom_text(aes(x = min(xlim_breaks) + 5, y = norm_y_lim[2], label = "Normal"), : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.#> Warning in geom_text(aes(x = min(xlim_breaks) + 5, y = 130, label = "Elevated"), : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.#> Warning in geom_text(aes(x = 90, y = 140, label = "S1"), color = "black", : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.#> Warning in geom_text(aes(x = s2_x_lim[2] - 5, y = s2_y_lim[2] - 5, label = "S2"), : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.#> Warning in geom_text(aes(x = min(xlim_breaks) + 5, y = 140, label = "ISH - S1"), : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.#> Warning in geom_text(aes(x = min(xlim_breaks) + 5, y = s2_y_lim[2] - 5, : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.#> Warning in geom_text(aes(x = 90, y = 90, label = "IDH\n S1"), color = "black", : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.#> Warning in geom_text(aes(x = s2_x_lim[2] - 5, y = 90, label = "IDH\n S2"), : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.#> Warning in geom_text(aes(x = xlim_breaks[xlim_breaks_length], y = ylim_breaks[ylim_breaks_length], : All aesthetics have length 1, but the data has 55 rows.#> ℹ Please consider using `annotate()` or provide this layer with data containing#> a single row.