6 The PLMScoRe object in R - Steph-Fulda/PLMScoRe GitHub Wiki
If you assigned the results of the startPLMScore
function to an object in R, this object will be available to you in R.
plmresult<-StartPLMScoRe()
In this particular case, the object will have the name plmresult, but you could have chosen any other name. The returned object plmresult is basically a list with four elements (described below):
- REMLogic specifications (RLs): This is itself another list that contains all the information that have been specified during the first interactive phase.
- Data: This is the data table that contains the data from the REMLogic event file plus additional variables that have been added by the program.
- Stats: A table with all the statistics that have been generated by PLMScoRe.
- IMI: Two vectors containing the intermovement intervals (IMI) of CLMS and CLMSnr. These are used to produce the IMI plots and are added here, in case you would want to change or customize these plots or compute further (group) statistics.
Being already in R, you have all the functionality of R available to you. So far, there is only one added method that is specifically dedicated to PLMScoRe:
- pprint(): will extract specific statistical indices from the Stats table, which may be useful for batch processing.
- For the R novice, there is a short introduction on manipulating and assessing R objects.
###REMLogic specifications (RLs)
If you have run the program, you have already seen large parts of the RLs object. In particular, at the end of the interactive input phase, this information had been printed to the console and you have been asked whether you want to change or save it. The complete RLs object this structure:
- Global:
- Legs:
- Leg leg channel name: name of the leg channel
- Right leg channel name: name of the leg channel
- Left leg LM event names: vector of event names
- Right leg LM event names: vector of event names
- Sleep:
- Sleep scored: 1 = yes, 0/NA=no
- Wake annotation name(s): vector of event names
- N1 annotation name(s): vector of event names
- N2 annotation name(s): vector of event names
- N3 annotation name(s): vector of event names
- REM annotation name(s): vector of event names
- Arousal:
- Arousal scored: 1 = yes, 0/NA=no
- Arousal event name(s):vector of event names
- Respiration:
- Respiratory events scored: 1 = yes, 0/NA=no
- Respiratory event name(s):vector of event names
- Start/Stop events:
- Start/Stop events present: 1 = yes, 0/NA=no
- Start/Lights off event name:vector of event names
- Stop/Lights on event name:vector of event names
- REMLogic text file specifications:
- Time format: string of time format specification
- Number of columns: number
- Start time column number: number
- Event column number: number
- Duration column number: number
- Channel location column number: number
- Sleep stage column number (if present): number or NA
- Annotations:
- All annotations: vector of event names
- Scored annotations: vector of event names
- Scored left leg annotations:vector of event names
- Scored right leg annotations: vector of event names
- Legs:
- Local:
- REMLogic event txt file: file path and name of file
- PLMS scoring rules: WASM 2016 (standard)
- Respiratory event related LM: 1= -2 to 10.25s, 2 = -0.5 to 0.5 s
- Output option(s): vector of one or more from: screen, csv, pdf
- Start: start time
- Stop: stop time
###Data The second element of the PLMScoRe object is the data table. This data table contains the information of the original REMLogic event txt file plus additional variables, created by PLMScoRe. The names of the variables are:
V1
,V2
,V3
,V4
,V6
: These are the original REMLogic event txt file variables sleep stage, position, start time, event, and channel, respectively, for a file with 6 columns. If your event file only contain 4 column you will only have channelsV1
,V2
,V4
.Dur
: Duration of event in decimal seconds. This variable has been taken from the event txt file (as V5/V3), but renamed by the program. The duration is taken directly from the txt file or computed in the case of bilateral LM.Time
: Start times as given in the original event txt file but formatted in a way that R can read it more easily.Onset
: Time of onset of event expressed as decimal seconds from the start of the recording (this makes computations in R much easier).Offset
: End of the event in decimal seconds from the start of the recording. Derived by addingDur
to theOnset
.nonCLM
: binary variable with 1 = nonCLM, 0 = CLM, NA = no LM.NoBil
: numeric variable, number of LM in bilateral LM: 0 = monolateral LM, > 1 = number in bilateral LM, NA = no LM.rLM
: binary variable. For LM: 1 = LM is respiratory event associated, 0 = non respiratory event associated. For respiratory events: 1 = has one or more CLM associated, NA = has no CLM associated. For all other events NA. The identification of respiratory event associations depend on the chosen definition (as asked for during the interactive phase).PLM
: binary variable with 1 = PLM, 0 = non PLM, NA = no LM, considering all LM.PLMnr
: binary variable with 1 = PLM, 0 = non PLM, NA = no LM, considering LM not associated to respiratory events.PLM_no
/PLMnr_no
: numeric variable, number of CLM in PLM/PLMnr series (1,2,3,...) or NA if not in PLM/PLMnr series or not LM.PLM_Sno
/PLMnr_Sno
: numeric variable, number of PLM/PLMnr series or NA if not in PLM/PLMnr series or not LM.PLM_l
/PLMnr_l
: numeric variable, length of current PLM/PLMnr series (number of single PLM in series) or NA if not in PLM/PLMnr series or not LM.IMI
/IMInr
: Intermovement interval in seconds, interval between the onset of the CLM and the onset of the previous CLM. As per WASM 2016 specification, no IMI is computed in the case that a nonCLM is between two CLM.AR
: binary variable with 1 = LM associated with arousal, 0 = all other events.Stage
: Sleep stage at the onset of the event (0, 1, 2, 3, 4 denote wake, N1, N2, N3, REM, respectively).
In addition, there are two further variables T
(class of event) and T2
(event) that are created internally to re-code all event in a standardized way:
T
= 1, sleep:T2
= 0,1,2,3,4 as wake, N1, N2, N3, REM
T
= 2, leg movements:T2
= 10, 11, 12 as left, right, bilateral LM
T
= 3, respiratory events:T2
= 20 as respiratory event
T
= 4, arousal events:T2
= 30 as arousal event.
T
= 5, start/stop or lights off/lights on events:T2
= 51, 52 as start/lights off and stop/lights on events.
###Statistics (Stats) The third element of the PLMScoRe object is a table with all numerical results as described in the previous section. This table has 10 variables and currently 318 rows. The variables are:
LMtype
: The type event for which the statistic is computed. These include the different types of LM, IMI, and information about sleep/wake periods, respiratory events and arousals (for a full description see previous section.Statistic
: The type of statistic that has been computed and which includes any of the following: number, no./hour, duration mean, SD, minimum, and maximum for LM; number, mean and SD for IMI; number and no./hour for arousals; number, no./hour, and %RLM for respiratory events.TIB
,TST
,Wake
,N1
,N2
,N3
,REM
,NREM
: These 8 variables contain the respective statistic for the respective outcome.
As described below the pprint() function can be used to extract one or more values from this table.
###Intermovement intervals (IMI)
The fourth and last element of the PLMScoRe object are the vectors of intermovement intervals of all candidate leg movements (CLM) during sleep (IMI) or all CLMSnr (IMInr), i.e. CLM during sleep that are not associated to respiratory events. As specified in the WASM 2016 rules, no IMI is computed if there is one or more nonCLM between two CLM.
This IMI are used to generate the IMI plots given in the screen and pdf output. They are included here, in case you would like to customize the IMI plots or generate group statistics. If you are not familiar with R, see below to find out how to assess these IMI vectors.
###pprint()
As described above, part of the PLMScoRe object and the csv output is a large table with all summary statistics that have been generated. The pprint()
function is a convenient way to select and return one or more of these statistics. The pprint()
function accepts four inputs:
- statt: the name of the table of LM statistics generate by
StartPLMScoRe
- sel: a vector of selection criteria as described below
- table: 1 = returns a table with column names (default), 0 = returns only the numeric values
- pretty: 1 = rounds all numbers to two decimal points (default), 0 = numbers are not rounded
The only mandatory input is statt, the LM statistic table. If sel
is not specified, the function returns all indices. If table
is not specified, a table with column names will be returned and if pretty
is not specified, all numbers will be round to 2 decimal points.
Let's say you started with
myplm<-StartPLMScoRe()
then after the function has run, myplm
will be PLMScoRe object that contains the LM statistic table, which you can extract into a new object (let's say you want to call it mystats) with
mystats<-myplm$Stats
As described above this statistic table includes the variables LMType
, Statistic
, and the sleep/wake stage TIB
, TST
, Wake
, N1
, N2
, N3
, REM
, and NREM
. You can extract single or multiple statistics by specifying in the selection criteria what you want to select. This can be one or more entries in the above entries on LMType, Statistic, and sleep/wake stages. If you do not specify all three the function will a return all available information that is found. For example:
> pprint(mystats, sel=c("PLM"))
LMtype Statistic TIB TST Wake N1 N2 N3 REM NREM
1 PLM no./hour 29.43 29.61 26.59 24.52 12.35 62.86 0.88 35.74
2 PLM number 202.00 191.00 11.00 19.00 28.00 143.00 1.00 190.00
3 PLM duration; mean 2.87 2.79 4.34 2.30 2.33 2.95 2.11 2.79
4 PLM duration; SD 1.29 1.10 2.77 1.58 0.88 1.03 NA 1.11
5 PLM duration; min 0.52 0.52 1.04 0.52 0.61 1.35 2.11 0.52
6 PLM duration; max 9.52 9.52 9.03 5.81 4.14 9.52 2.11 9.52
Will return all statistics in all sleep/stages for PLM. Selection criteria can be combined as wanted:
# PLM statistics during TST
> pprint(mystats, sel=c("PLM", "TST"))
LMtype Statistic TST
1 PLM no./hour 29.61
2 PLM number 191.00
3 PLM duration; mean 2.79
4 PLM duration; SD 1.10
5 PLM duration; min 0.52
6 PLM duration; max 9.52
# All PLM/PLMnr and CLM/CLMnr indices during REM
> pprint(mystats, sel=c("PLM", "PLMnr", "CLM", "CLMnr", "no./hour", "REM"))
LMtype Statistic REM
1 CLM no./hour 16.76
2 CLMnr no./hour 15.88
3 PLM no./hour 0.88
4 PLMnr no./hour 0.88
There are two further specifications that can be made. Per default pretty = 1
which means that numeric output is rounded. If you do not want this, you can specify pretty = 0
. For comparison:
> pprint(mystats, sel=c("CLM"))
LMtype Statistic TIB TST Wake N1 N2 N3 REM NREM
1 CLM no./hour 43.71 40.78 89.43 51.61 26.03 63.74 16.76 45.89
2 CLM number 300.00 263.00 37.00 40.00 59.00 145.00 19.00 244.00
3 CLM duration; mean 2.89 2.73 4.02 2.51 2.42 2.95 2.43 2.75
4 CLM duration; SD 1.70 1.43 2.75 1.87 1.39 1.03 2.55 1.31
5 CLM duration; min 0.50 0.50 0.53 0.50 0.51 1.35 0.52 0.50
6 CLM duration; max 11.94 11.30 11.94 6.51 6.92 9.52 11.30 9.52
> pprint(mystats, sel=c("CLM"), pretty=0)
LMtype Statistic TIB TST Wake N1 N2 N3 REM NREM
1 CLM no./hour 43.708009 40.775194 89.430164 51.612903 26.029412 63.736264 16.764706 45.893417
2 CLM number 300.000000 263.000000 37.000000 40.000000 59.000000 145.000000 19.000000 244.000000
3 CLM duration; mean 2.886219 2.726920 4.018534 2.514586 2.420760 2.949444 2.426432 2.750318
4 CLM duration; SD 1.697243 1.427958 2.753592 1.872345 1.392534 1.025569 2.553381 1.306883
5 CLM duration; min 0.500000 0.500000 0.530000 0.500000 0.510000 1.350000 0.520000 0.500000
6 CLM duration; max 11.935331 11.297393 11.935331 6.509261 6.917783 9.520000 11.297393 9.520000
Finally, the standard output will contain the column names and also print the variables LMtype
and Statistic
. If you want to suppress that, you can specify table = 0
. This canbe useful in the case of batch processing.
> pprint(mystats, sel=c("PLM", "TST", "no./hour"))
LMtype Statistic TST
1 PLM no./hour 29.61
> pprint(mystats, sel=c("PLM", "TST", "no./hour"), table=0)
[1] 29.61
###Assessing R objects
If this is your first time using R, here are some pointers to get you started. I will assume that you start at zero. In my experience, 80% of all first time errors are due to simple typos. My first advice is therefore to work with scripts instead of using the command line. You open a new script by choosing File -> New File -> R script
from the menu at the top of the R window. You can then type your commands into this script (or copy and paste them from this or other documents) and execute it in R by positioning your cursor somewhere in the line and pressing Strg+R
. This will paste the respective line to the command window and execute it. An additional benefit is that you can save that script for later use. Often it is easier and faster to modify an existing script than to write a new one. If you want to comment your script use the #
sign before the comment. Everything after the #
in the same line will be pasted to the console but not executed.
In R, objects have the name that you give them and you can name everything you want. You assign something to a named object with the <-
sign. For example:
# Assign numerical values to x and y
> x <- 2
> y <- 3
# Show the values of x and y
> x
[1] 2
> y
[1] 3
# You can now do other stuff with your names objects
> x + y
[1] 5
> z <- x + y
> z
[1] 5
There are many different objects but the most used are vectors, matrices, and lists (see here for an introduction). A vector is a string of elements such as 1,2,3. All elements of the vector are of the same type, so it can be a vector of numbers or a vector of names. If the elements are both numbers and names, R will treat also the numbers as names. Elements of a vector are concatenated or combined by the function c()
.
> v1 <- c(1,2,3)
> v2 <- c(1, 100, 9)
> v3 <- c("a", "b", "c")
> v4 <- c(v1, v2)
Elements of a vector are extracted with the square brackets [ ]
. If you have a vector v1 of length 10, the 5th element will be assessed with v1[5]
, multiple elements can be assessed with v1[1:3]
or v1[c(1,3,7)]
.
> v1 <- c(1:20)
> v1[6]
[1] 6
> v1[18:19]
[1] 18 19
> v1[c(1, 6, 14)]
[1] 1 6 14
> v1[15:10]
[1] 15 14 13 12 11 10
> v1[c(1:3, 6, 11:10)]
[1] 1 2 3 6 11 10
A matrix is a rectangular table with a number of rows and columns. A data frame is a matrix where the columns (and possible rows) have additional attributes such as column or variable names. Also a matrix can have only one data type (numeric, string) while a data frame can contain both numeric and character variables.
The concept of a matrix is one of the fundamental differences between R and matlab!
# Generate a matrix
> m1 <- matrix(NA, 4, 2) # generates a 4 (rows) x 2 (columns) matrix filled with "NA"
> m2 <- matrix(c(1:4), 2, 2) # generates a 2 x 2 matrix with the numbers 1 to 4
# Assessing matrix elements
> m1[1,1] # extracts the first element of the first column
> m1[1,2] # first element of the second column
> m1[2,2] # second element of the second column
> m1[1,] # the first row
> m1[,1] # the first column
In a data frame which is also a rectangular table, the single elements can be assessed the same way. In addition, since data frames have variable names there are additional ways:
# Let's suppose we have a 4 x 2 data frame (d) with the variable (column names) var1 and var 2
# show the variable names
> names(d)
[1] var1 var2
# Assessing elements
> d$var1 # all values of variable 1
> d$var1[2] # the second element of variable 1
Finally, lists can have any structure including sub-lists, which makes them very flexible. Elements of a list can or cannot be named.
# Generate a list
l1 <- list(a=c(1:10), b=c("a", "b"))
l2 <- list(d=c("z", 4), l1)
# Assessing elements of lists
# Elements can be assessed by name or position (with [ ](/Steph-Fulda/PLMScoRe/wiki/-))
> l2[1](/Steph-Fulda/PLMScoRe/wiki/1)
[1] "z" "4"
> l2$d
[1] "z" "4"
# Elements of a list within a list
> l2[2](/Steph-Fulda/PLMScoRe/wiki/2)
$a
[1] 1 2 3 4 5 6 7 8 9 10
$b
[1] "a" "b"
> l2[2](/Steph-Fulda/PLMScoRe/wiki/2)$a
[1] 1 2 3 4 5 6 7 8 9 10
> l2[2](/Steph-Fulda/PLMScoRe/wiki/2)$a[1]
[1] 1
> l2[2](/Steph-Fulda/PLMScoRe/wiki/2)[1](/Steph-Fulda/PLMScoRe/wiki/1)[1]
[1] 1
How can this help when you work with PLMScoRe? First, you can extract and view specific results or parameters. Second, you can also change these directly and third, you can manipulate and process data generated with PLMScoRe for your specific purposes. Let's start with extracting and viewing results or parameters. Remember that you assigned the PLMScoRe objects to a named object at the beginning ('plmresult<-startPLMScoRe()').
#The resulting object "plmresult" is a list with 4 elements
> mode(plmresult)
[1] "list"
> length(plmresult)
[1] 4
> names(plmresult)
[1] "RLs" "Data" "Stats" "IMI"
#The first element RLs (the REMLogic specifications) is in itself a named list
> mode(plmresult$RLs)
[1] "list"
> length(plmresult$RLs)
[1] 2
> names(plmresult$RLs)
[1] "Global" "Local"
#Each of these (Global/Local) is again a list
> mode(plmresult$RLs$Global)
[1] "list"
> length(plmresult$RLs$Global)
[1] 7
> names(plmresult$RLs$Global)
[1] "Legs" "Sleep"
[3] "Arousal" "Respiration"
[5] "Start/Stop events" "REMLogic text file specifications"
[7] "Annotations"
#Some more nested levels further, we finally see a basic entry
> plmresult$RLs$Global$Legs$`Left leg channel name`
[1] "EMG.Tibialis-Leg.Left"
#You can access this entry by name (as above) or by position within the object
> plmresult[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)
[1] "EMG.Tibialis-Leg.Left"
Often it is easier and less error prone to access entries by their position rather than by name. The RLs object also contains two tables, one for the extracted indices and statistical descriptors, one for the data.
#The statistics table is the third element
> names(plmresult)
[1] "RLs" "Data" "Stats" "IMI"
#The size of the table can be queried with
> dim(plmresult$Stats)
[1] 318 10
#which is the number of rows (318) and the number of variables/columns (10)
#Because it is a table, to show it use:
> View(plmresult$Stats)
#This will open a new window showing you the complete table with the possibility to scroll through the data
#This also works for the other table, the data
> View(plmresult$Data)
#It won't work well for objects that are not tables, for example the IMI vector
> View(plmresult$IMI$IMInr)
#try it and be disappointed...
Since you can access/select specific elements, you can also change them. While it makes little sense to change the results and the data, you might want change selected elements of the RL specifications, especially if you want to do batch processing and there are important differences between data sets.
#Let's say you want to change the names of the left leg channel
> plmresult[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)
[1] "EMG.Tibialis-Leg.Left"
#For some of the records the channel names was "ANT1-ANT2-L"
> plmresult[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)<-c("ANT1-ANT2-L")
> plmresult[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)
[1] "ANT1-ANT2-L"
#Let's say you want to add another arousal annotation
> plmresult$RLs$Global$Arousal$`Arousal event names`
[1] "AROUSAL" "AROUSAL-APNEA" "AROUSAL-DESAT" "AROUSAL-HYPOPNEA" "AROUSAL-LM"
[6] "AROUSAL-PLM" "AROUSAL-RERA" "AROUSAL-RESP" "AROUSAL-SNORE"
#The same result would be achieved with plmresult[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[3](/Steph-Fulda/PLMScoRe/wiki/3)[2](/Steph-Fulda/PLMScoRe/wiki/2)
#You want to add the annotation "AROUSAL-SPONT"
> plmresult[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[3](/Steph-Fulda/PLMScoRe/wiki/3)[2](/Steph-Fulda/PLMScoRe/wiki/2)<-c(plmresult[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[3](/Steph-Fulda/PLMScoRe/wiki/3)[2](/Steph-Fulda/PLMScoRe/wiki/2), "AROUSAL-SPONT")
> plmresult[1](/Steph-Fulda/PLMScoRe/wiki/1)[1](/Steph-Fulda/PLMScoRe/wiki/1)[3](/Steph-Fulda/PLMScoRe/wiki/3)[2](/Steph-Fulda/PLMScoRe/wiki/2)
[1] "AROUSAL" "AROUSAL-APNEA" "AROUSAL-DESAT" "AROUSAL-HYPOPNEA" "AROUSAL-LM"
[6] "AROUSAL-PLM" "AROUSAL-RERA" "AROUSAL-RESP" "AROUSAL-SNORE" "AROUSAL-SPONT"
Finally, you may want to manipulate or process the results of PLMScoRe. For example, you do not like the plot of the IMI distribution and want to create one of your own. Or, you want to generate an across subjects averaged IMI distribution for a publication.
#There are two vectors with the sequence of all IMIs between CLMS during sleep
> names(plmresult$IMI)
[1] "IMInr" "IMI"
#"IMI" are all CLMS, "IMInr" are only CLMSnr, i.e. not respiratory event related
#You plot a histogram with:
> hist(plmresult$IMI$IMI)
#which looks terrible
#BTW, if you do not want to type so much, you can also first extract the object and than use that single object
> IMI<-plmresult$IMI$IMI
> hist(IMI)
#still, looks terrible
#let's restrict it to the 1-90s range with each bin being 2 s and while we are at at, make it blue
> hist(IMI[IMI<=90], breaks=seq(0,90,2), col="blue")
#Better, but I am sure you can improve on that!!
See here, for basic help on histograms. If you want to go further, there are also very many free, accessible and very gentle introductions to R (see here).