Despite the prevalence of sleep complaints among psychiatric patients, few questionnaires have been specifically designed to measure sleep quality in clinical populations. The Pittsburgh Sleep Quality Index (PSQI) is a self-rated questionnaire which assesses sleep quality and disturbances over a 1-month time interval. Nineteen individual items generate seven “component” scores: subjective sleep quality, sleep latency, sleep duration, habitual sleep efficiency, sleep disturbances, use of sleeping medication, and daytime dysfunction. The sum of scores for these seven components yields one global score.
component | name | description |
---|---|---|
1 | subjective sleep quality | Answer to q 6 |
2 | sleep latency | Scaled sum of number of minutes before sleep (q 2) and evaluation of sleep within 30min (q 5a), scaled to a 5 point scale |
3 | sleep duration | Scaled score of number of hours before one falls asleep (q 4), scaled to a 5 point scale |
4 | habitual sleep efficiency | hours of sleep (q 4) divided by bedtime (q 1) subtracted from rising time (q 3), and scaled to a 5 point scale |
5 | sleep disturbances | Sum of evaluation of sleep within 30min (q 5a) and all remaining questions on sleep problems (q 5b-j), scaled to a 5 point scale |
6 | use of sleeping medication | Answer to question on use of sleep medication (q 7) |
7 | daytime dysfunction | Sum of evaluation of staying awake (q 8) and evaluation of keeping enthusiastic (q 9), scaled to a 5-point scale |
global score | sum of the above. | If any of the above is not possible to calculate, the global sum is also not calculated |
Questions with multiple subquestions should be named in a similar manner, suffixed by the alphabetical index (psqi_5a, psqi_5b etc.). For questions 5j and 10j, the frequency of occurence should have the names psqi_5j and psqi_10e, and the freehand explanations should have any type of suffix after this to indicate a text answers (i.e. psqi_5j_Desc or psqi_5j_string, psqi_5j_freehand). As an example, LCBC has the following set-up:
psqi
functions
All the functions necessary to compute the components and the global
score for the psqi are included in this package, and loading it in your
R-environment makes them accessible to you. All the functions for the
PSQI, are prefixed with psqi_
, so they are easy to
find.
There are a total of 7 psqi_
functions in this package.
One for each component, except 1 and 6 which are just the raw answers
from questions 4 and 7. In addition, there is a function to calculate
the global, which must be done after having computed the 7
components. Lastly, there is a function psqi_compute
which
will do it all for you, compute all the components and the global. All
the functions are documented as any function in R, and so you may call
?psqi_compute
to see the user documentation for the
functions.
There are many options you can utilize for these functions, so you
may explore them as you wish. The functions are also made such that you
can use them on datasets other than the LCBC MOAS dataset, but you will
then have to specify the columns to use for the various components your
self. The functions are made to work directly with the MOAS, and require
no extra input other that the object containing the MOAS to run all
calculations. Running psqi_compute(MOAS)
will calculate all
components and append them to the data you provided. Running
psqi_compute(MOAS, keep_all = FALSE)
will only return the
computed components and the global score in a data frame.
library(questionnaires)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
# Create some data to test on
data <- data.frame(
psqi_01 = c("22:30:00", "07:00:00", "22:30:00", "NaN", "23:30:00"),
psqi_02 = c(5, 10, 30, 20, 30),
psqi_03 = c("05:50:00", "17:00:00", "06:30:00", "NaN", "06:45:00"),
psqi_04 = c(7, 9.75, 8, 9, 6),
psqi_05a = c(0L, 0L, 1L, 2L, 2L),
psqi_05b = c(0L, 0L, 2L, 0L, 2L),
psqi_05c = c(0L, 0L, 3L, 1L, 2L),
psqi_05d = c(0L, 0L, 0L, 0L, 0L),
psqi_05e = c(0L, 3L, 1L, 0L, 0L),
psqi_05f = c(0L, 0L, 1L, 0L, 1L),
psqi_05g = c(0L, 0L, 1L, 1L, 3L),
psqi_05h = c(0L, 0L, 0L, 0L, 0L),
psqi_05i = c(0L, 0L, 0L, 0L, 0L),
psqi_05j_Coded = c(NA,NA, "No", NA, NA),
psqi_05j = c(0L, 0L, NA, NA, NA),
psqi_06 = c(0L, 1L, 2L, 1L, 1L),
psqi_07 = c(0L, 0L, 0L, 0L, 0L),
psqi_08 = c(0L, 0L, 0L, 1L, 0L),
psqi_09 = c(0L, 0L, 1L, 0L, 1L),
psqi_10 = c(3L, 1L, 3L, NA, 3L),
psqi_10a = c(1L, NA, 1L, NA, 0L),
psqi_10b = c(0L, NA, 0L, NA, 0L),
psqi_10c = c(2L, NA, 2L, NA, 0L),
psqi_10d = c(0L, NA, 0L, NA, 0L),
psqi_10e_Coded = c(NA, NA, "", NA, NA),
psqi_10e = c(NA_integer_, NA_integer_, NA_integer_, NA_integer_, NA_integer_),
psqi_11a = c(0L, 2L, 1L, 1L, 2L),
psqi_11b = c(0L, 0L, 2L, 0L, 2L),
psqi_11c = c(0L, 0L, 1L, 1L, 2L),
psqi_11d = c(0L, 3L, 2L, 0L, 2L)
)
data
#> psqi_01 psqi_02 psqi_03 psqi_04 psqi_05a psqi_05b psqi_05c psqi_05d
#> 1 22:30:00 5 05:50:00 7.00 0 0 0 0
#> 2 07:00:00 10 17:00:00 9.75 0 0 0 0
#> 3 22:30:00 30 06:30:00 8.00 1 2 3 0
#> 4 NaN 20 NaN 9.00 2 0 1 0
#> 5 23:30:00 30 06:45:00 6.00 2 2 2 0
#> psqi_05e psqi_05f psqi_05g psqi_05h psqi_05i psqi_05j_Coded psqi_05j psqi_06
#> 1 0 0 0 0 0 <NA> 0 0
#> 2 3 0 0 0 0 <NA> 0 1
#> 3 1 1 1 0 0 No NA 2
#> 4 0 0 1 0 0 <NA> NA 1
#> 5 0 1 3 0 0 <NA> NA 1
#> psqi_07 psqi_08 psqi_09 psqi_10 psqi_10a psqi_10b psqi_10c psqi_10d
#> 1 0 0 0 3 1 0 2 0
#> 2 0 0 0 1 NA NA NA NA
#> 3 0 0 1 3 1 0 2 0
#> 4 0 1 0 NA NA NA NA NA
#> 5 0 0 1 3 0 0 0 0
#> psqi_10e_Coded psqi_10e psqi_11a psqi_11b psqi_11c psqi_11d
#> 1 <NA> NA 0 0 0 0
#> 2 <NA> NA 2 0 0 3
#> 3 NA 1 2 1 2
#> 4 <NA> NA 1 0 1 0
#> 5 <NA> NA 2 2 2 2
There are 7 components in the PSQI, and the functions in the package allow you to calculate 5 components based on two or more columns in the data. Two components do not have their own functions as they are just the raw answers to two questions.
Each component has unique arguments needed to calculate.
psqi_compute_comp2(min_before_sleep = data$psqi_02,
no_sleep_30min = data$psqi_05a)
#> [1] 0 0 1 2 2
psqi_compute_comp3(hours_sleep = data$psqi_04)
#> [1] 1 0 0 0 1
psqi_compute_comp4(hours_sleep = data$psqi_04,
bedtime = data$psqi_01,
risingtime = data$psqi_03)
#> [1] NA NA NA NA NA
# Requires many columns, so for conveniece must be specified in another way than others.
psqi_compute_comp5(data = data,
sleep_troubles = matches("^psqi_05[b-j]$"))
#> [1] 0 1 1 1 1
psqi_compute_comp7(keep_awake = data$psqi_08,
keep_enthused = data$psqi_09)
#> [1] 0 0 1 1 1
If you want to add these directly to your data.frame, you can wrap
then insire a dplyr::mutate
.
data2 <- data %>%
mutate(
comp1 = psqi_06,
comp2 = psqi_compute_comp2(min_before_sleep = psqi_02,
no_sleep_30min = psqi_05a),
comp3 = psqi_compute_comp3(hours_sleep = psqi_04),
comp4 = psqi_compute_comp4(hours_sleep = psqi_04,
bedtime = psqi_01,
risingtime = psqi_03),
comp5 = psqi_compute_comp5(data = data,
sleep_troubles = matches("^psqi_05[b-j]$")),
comp6 = psqi_07,
comp7 = psqi_compute_comp7(keep_awake = psqi_08,
keep_enthused = psqi_09)
)
data2
#> psqi_01 psqi_02 psqi_03 psqi_04 psqi_05a psqi_05b psqi_05c psqi_05d
#> 1 22:30:00 5 05:50:00 7.00 0 0 0 0
#> 2 07:00:00 10 17:00:00 9.75 0 0 0 0
#> 3 22:30:00 30 06:30:00 8.00 1 2 3 0
#> 4 NaN 20 NaN 9.00 2 0 1 0
#> 5 23:30:00 30 06:45:00 6.00 2 2 2 0
#> psqi_05e psqi_05f psqi_05g psqi_05h psqi_05i psqi_05j_Coded psqi_05j psqi_06
#> 1 0 0 0 0 0 <NA> 0 0
#> 2 3 0 0 0 0 <NA> 0 1
#> 3 1 1 1 0 0 No NA 2
#> 4 0 0 1 0 0 <NA> NA 1
#> 5 0 1 3 0 0 <NA> NA 1
#> psqi_07 psqi_08 psqi_09 psqi_10 psqi_10a psqi_10b psqi_10c psqi_10d
#> 1 0 0 0 3 1 0 2 0
#> 2 0 0 0 1 NA NA NA NA
#> 3 0 0 1 3 1 0 2 0
#> 4 0 1 0 NA NA NA NA NA
#> 5 0 0 1 3 0 0 0 0
#> psqi_10e_Coded psqi_10e psqi_11a psqi_11b psqi_11c psqi_11d comp1 comp2 comp3
#> 1 <NA> NA 0 0 0 0 0 0 1
#> 2 <NA> NA 2 0 0 3 1 0 0
#> 3 NA 1 2 1 2 2 1 0
#> 4 <NA> NA 1 0 1 0 1 2 0
#> 5 <NA> NA 2 2 2 2 1 2 1
#> comp4 comp5 comp6 comp7
#> 1 NA 0 0 0
#> 2 NA 1 0 0
#> 3 NA 1 0 1
#> 4 NA 1 0 1
#> 5 NA 1 0 1
After having all components calculated, the global is the sum of all
these. The psqi_compute_global
function takes an entire
data frame, and a tidy-selected collection of columns of the calculated
components. In this case we called the components with names starting
with “Comp”, and can use this logic for the computation.
psqi_compute_global(data2, starts_with("comp"))
#> [1] NA NA NA NA NA
We get one NA
because someone has omitted to answer to a
question, and so a component is missing, and thus a global cannot be
computed. You have the option to specify how many missing components the
global calculation will allow, but remember that this will skew the data
somewhat.
psqi_compute_global(data2, starts_with("comp"), max_missing = 2)
#> [1] 1.166667 2.333333 5.833333 5.833333 7.000000
It is quite cumbersome to mutate and fix every component manually, so
the psqi_compute
function is there to make everything in
one go. If you are working on MOAS-like data, this is easy to use, as
column names and specifications are pre-set to work with the MOAS. If
you are working on other data, you will need to set each option to the
column names in the data for each question manually.
psqi_compute(data)
#> psqi_01 psqi_02 psqi_03 psqi_04 psqi_05a psqi_05b psqi_05c psqi_05d
#> 1 22:30:00 5 05:50:00 7.00 0 0 0 0
#> 2 07:00:00 10 17:00:00 9.75 0 0 0 0
#> 3 22:30:00 30 06:30:00 8.00 1 2 3 0
#> 4 NaN 20 NaN 9.00 2 0 1 0
#> 5 23:30:00 30 06:45:00 6.00 2 2 2 0
#> psqi_05e psqi_05f psqi_05g psqi_05h psqi_05i psqi_05j_Coded psqi_05j psqi_06
#> 1 0 0 0 0 0 <NA> 0 0
#> 2 3 0 0 0 0 <NA> 0 1
#> 3 1 1 1 0 0 No NA 2
#> 4 0 0 1 0 0 <NA> NA 1
#> 5 0 1 3 0 0 <NA> NA 1
#> psqi_07 psqi_08 psqi_09 psqi_10 psqi_10a psqi_10b psqi_10c psqi_10d
#> 1 0 0 0 3 1 0 2 0
#> 2 0 0 0 1 NA NA NA NA
#> 3 0 0 1 3 1 0 2 0
#> 4 0 1 0 NA NA NA NA NA
#> 5 0 0 1 3 0 0 0 0
#> psqi_10e_Coded psqi_10e psqi_11a psqi_11b psqi_11c psqi_11d
#> 1 <NA> NA 0 0 0 0
#> 2 <NA> NA 2 0 0 3
#> 3 NA 1 2 1 2
#> 4 <NA> NA 1 0 1 0
#> 5 <NA> NA 2 2 2 2
#> psqi_comp1_quality psqi_comp2_latency psqi_comp3_duration
#> 1 0 0 1
#> 2 1 0 0
#> 3 2 1 0
#> 4 1 2 0
#> 5 1 2 1
#> psqi_comp4_efficiency psqi_comp5_problems psqi_comp6_medication
#> 1 NA 0 0
#> 2 NA 1 0
#> 3 NA 1 0
#> 4 NA 1 0
#> 5 NA 1 0
#> psqi_comp7_tired psqi_global
#> 1 0 NA
#> 2 0 NA
#> 3 1 NA
#> 4 1 NA
#> 5 1 NA
psqi_compute(data, keep_all = FALSE)
#> psqi_comp1_quality psqi_comp2_latency psqi_comp3_duration
#> 1 0 0 1
#> 2 1 0 0
#> 3 2 1 0
#> 4 1 2 0
#> 5 1 2 1
#> psqi_comp4_efficiency psqi_comp5_problems psqi_comp6_medication
#> 1 NA 0 0
#> 2 NA 1 0
#> 3 NA 1 0
#> 4 NA 1 0
#> 5 NA 1 0
#> psqi_comp7_tired psqi_global
#> 1 0 NA
#> 2 0 NA
#> 3 1 NA
#> 4 1 NA
#> 5 1 NA
psqi_compute(data, keep_all = FALSE, max_missing = 1)
#> psqi_comp1_quality psqi_comp2_latency psqi_comp3_duration
#> 1 0 0 1
#> 2 1 0 0
#> 3 2 1 0
#> 4 1 2 0
#> 5 1 2 1
#> psqi_comp4_efficiency psqi_comp5_problems psqi_comp6_medication
#> 1 NA 0 0
#> 2 NA 1 0
#> 3 NA 1 0
#> 4 NA 1 0
#> 5 NA 1 0
#> psqi_comp7_tired psqi_global
#> 1 0 1.166667
#> 2 0 2.333333
#> 3 1 5.833333
#> 4 1 5.833333
#> 5 1 7.000000
Buysse et al. (1989) The Pittsburgh sleep quality index: A new instrument for psychiatric practice and research, Psychiatry Research, 28:2, 193-213