-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbutton_press.Rmd
248 lines (200 loc) · 11 KB
/
button_press.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
---
title: 'RQ1: button press and wearlog'
author: "Carolina Guidolin"
date: "2024-03-27"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# Note
This document should be run after 01_import and 02_datapreparation.
# Aim
Here, we will be testing the concordance between two sources of non-wear: the button press and the wear log entries.
Note that the following code chunks build on variables and dfs generated by the nonwear_source_fusion_wrlg.Rmd file and should thus be run after it.
## Cleaning the dataset and matching the dataset of the button press events and that of the wear log events
```{r}
#First, we filter the dataframe for rows that were a button press was performed (EVENT = 1)
data.bp <- dataset.LL.wrlg %>%
filter(EVENT == "1")
#If a button press occurred within the same minute (60s), we want to eliminate it as likely the participants pressed it multiple times by accident
data.bp.clean <- data.bp %>%
group_by(Id) %>%
filter(abs(difftime(lead(Datetime, default = first(Datetime)), Datetime, units = "secs")) >= 60) %>% #default = first(Datetime) is set for instances where no next Datetime exists. E.g. for the last timestamp of each group (Id)
ungroup()
#For the "off" intervals calculated in joined_int, calculate timestamp corresponding to the start of the interval
wrlg_int_start <- wrlg_int %>%
filter(!is.na(State)) %>% #the first Interval is NA for all participants (see LightLogR sc2interval function)
filter(State == "off") %>%
mutate(start = lubridate::int_start(Interval))
#For the "off" intervals calculated in joined_int, calculate timestamp corresponding to the end of the interval
wrlg_int_end <- wrlg_int %>%
filter(!is.na(State)) %>% #the first Interval is NA for all participants (see LightLogR sc2interval function)
filter(State == "off") %>%
mutate(end = lubridate::int_end(Interval))
```
## Creating a function which classifies the wear log detected "off" intervals based on the presence of a button press at both ends of the interval.
1. Unbounded: button press missing ither at both ends or only at one end
2. Bounded: button press at both ends of the interval
```{r}
classify_interval <- function(has_start, has_end) {
if (has_start && has_end) {
return("bounded")
} else if (!has_start && has_end) {
return("unbounded")
} else if (has_start && !has_end) {
return("unbounded")
} else {
return("unbounded")
}
}
#Function to check if an event falls within the specified window at both extremities of the interval
event_within_window <- function(event_time, interval_start, interval_end) {
within_window <- (event_time >= interval_start - window) | (event_time <= interval_end + window)
return(within_window)
}
```
## Finding the nearest button press to each timestamp for an "off" entry on the wear log
We use data.tables package to perform a rolling join and match the Datetime of one dataset to the nearest Datetime of the other dataset. Data tables are normal data frames, but they have extra features which allow to work better with them.
```{r}
data.table::setDT(data.bp.clean)[, join_date := Datetime] #convert to data.table and append original column that we want to keep
data.table::setDT(wrlg_int_start)[, join_date := start] #convert to data.table and append original column that we want to keep
data.table::setDT(wrlg_int_end)[, join_date := end] #convert to data.table and append original column that we want to keep
window_sizes <- c(1, 2, 3, 4, 5, 6, 7, 8)
interval_checker <- lapply(window_sizes, function(window) {
#Find the nearest button press for the start of each "off" wear log interval, based on the specified time window
bp_wearlog_start <- data.bp.clean[wrlg_int_start, on = .(Id, join_date), roll = "nearest"] %>%
#Perform a left join of the two datasets, for each row of wrlg_int (wear log entries) the nearest data.bp.clean timestamp (button press) is found
.[, tmp_diff := as.numeric(difftime(Datetime, join_date, units = "mins"))] %>%
#Calculate difference in minutes between the two datetime columns
.[, has_start := tmp_diff <= window] %>%
#Check if difference is less than or equal to window minutes and store it as TRUE or FALSE
.[, .(Id, Datetime, i.State, tmp_wearlog_start = join_date, tmp_diff, has_start)] %>% #select cols of interest
rename(tmp_bp_start = Datetime)
#Find the nearest button press for the end of each "off" wear log interval - based on the specified time window
bp_wearlog_end <- data.bp.clean[wrlg_int_end, on = .(Id, join_date), roll = "nearest"] %>%
#Perform a left join of the two datasets, for each row of wrl_int (wear log entries) the nearest data.bp.clean timestamp (button press) is found
.[, tmp_diff := as.numeric(difftime(Datetime, join_date, units = "mins"))] %>%
#Calculate difference in minutes between the two datetime columns
.[, has_end := tmp_diff <= window] %>%
#Check if difference is less than or equal to window minutes and store it as TRUE or FALSE
.[, .(Id, Datetime, i.State, tmp_wearlog_end = join_date, tmp_diff, has_end)] %>% #select cols of interest
rename(tmp_bp_end = Datetime,
i.Id = Id)
#Result: has_start is TRUE for "off" wear log intervals where a button press is present within time window of interest, at the start of the interval; has_end is TRUE for "off" wear log intervals where a button press is present within time window of interest, at the end of the interval.
#Based on this, we can classify intervals based on the presence of a button press using the function classify_interval
#First, we combine the two data tables (start and end)
bp_wrlg_df <- bp_wearlog_start %>%
cbind(bp_wearlog_end) %>%
select(Id, tmp_bp_start, tmp_bp_end, tmp_wearlog_start, tmp_wearlog_end, has_start, has_end)
#Add a column called classification that returns one of the two types of interval: 1) bounded, 2) unbounded
bp_wrlg_df$classification <- apply(bp_wrlg_df[, c("has_start", "has_end")], 1, function(x) {
classify_interval(x[1], x[2])
})
bp_wrlg_df$window_size <- window #add a column which contains the information of the window size utilised
return(bp_wrlg_df)
})
#Now the results are stored in interval_checker, which is a list. We'd like to store this into a dataframe and calculate the number of interval types for each window size
bd_wrlg_df <- bind_rows(interval_checker) %>%
group_by(window_size) %>%
count(classification) %>%
mutate(n_per = (n/198)*100) #calculate percentage of each interval type based on total number of interval, which we know is 198
```
#Plotting the results
Her, we plot % of open intervals and % of closed intervals based on different window sizes.
```{r}
#Turn data in wide format for plotting
bp_wrlg_df_wide <- bd_wrlg_df %>%
pivot_wider(names_from = classification, values_from = c(n, n_per))
#Create a continuous colour palette
val <- c("1" = "#00366C",
"2" = "#00467D",
"3" = "#005691",
"4" = "#0067A7",
"5" = "#0078BD",
"6" = "#1581C6",
"7" = "#5898D6",
"8" = "#73A7E0") #self made palette
#Plot
wrlg_int_classified <- ggplot(bp_wrlg_df_wide, aes(x = n_per_bounded, y = n_per_unbounded, colour = as.factor(window_size))) +
geom_point(position = position_jitter(w=0.3),
alpha =0.5, size=2.5) +
xlim(0, 100) +
ylim(0, 100) +
scale_color_manual(values = val,
breaks = c("1", "2", "3", "4", "5", "6", "7", "8")) +
labs(x = "Closed intervals (%)", y = "Open intervals (%)", color = "Window size\n(minutes)") +
theme_bw() +
geom_rect(aes(xmin = 60.0, xmax = Inf, ymin = -Inf, ymax = 40.0), fill = "#FFC20A", colour = NA, alpha = 0.01) +
theme(plot.title = element_text(hjust = 0.5, size = 24),
axis.text = element_text(size = 16),
axis.title = element_text(size = 18),
legend.title = element_text(size = 14, hjust =0.5),
legend.text = element_text(size = 14)) +
coord_fixed(ratio = 1) +
ggpubr::rremove("xylab") +
guides(color = guide_legend(nrow =1))
#Zoom in the yellow shaded area
wrlg_int_classified_zoom <- ggplot(bp_wrlg_df_wide, aes(x = n_per_bounded, y = n_per_unbounded, colour = as.factor(window_size))) +
geom_point(position = position_jitter(w=0.3),
alpha =0.5, size=4) +
xlim(60.0, 100) +
ylim(0, 40.0) +
scale_color_manual(values = val,
breaks = c("1", "2", "3", "4", "5", "6", "7", "8")) +
labs(x = "Closed intervals (%)", y = "Open intervals (%)", color = "Window size\n(minutes)") +
theme_bw() +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf), fill = "#FFC20A", colour = NA, alpha = 0.01) +
theme(plot.title = element_text(hjust = 0.5),
axis.text = element_text(size = 14),
axis.title = element_text(size = 14),
legend.title = element_text(size = 14),
legend.text = element_text(size = 14)) +
coord_fixed(ratio = 1) +
ggpubr::rremove("xylab")
plot <- ggpubr::ggarrange(wrlg_int_classified, wrlg_int_classified_zoom,
ncol = 2,
nrow = 1,
common.legend = TRUE,
legend = "top",
widths = c(1,1),
heights = c(1,1),
align = "hv")
wrlg_int_type_multipanel <- ggpubr::annotate_figure(plot,
top = text_grob("Wake 'off' interval type according to button press event",
color = "black",
face = "plain",
size = 18,
hjust = 0.5),
left = text_grob("Open intervals (%)",
size = 14,
rot = 90,
hjust = 0.6),
bottom = text_grob("Closed intervals (%)",
size = 14,
hjust = 0.5))
print(wrlg_int_type_multipanel)
```
#Saving the above plots
```{r}
ggsave(filename = "wrlg-int-classified.png",
plot = wrlg_int_classified,
width = 10,
height = 8,
dpi = 600,
path= "G:/cyepi/code/outputs/button_press")
ggsave(filename = "wrlg-int-zoom.png",
plot = wrlg_int_classified_zoom,
width = 10,
height = 8,
dpi = 600,
path= "G:/cyepi/code/outputs/button_press")
## Figure for paper
ggsave(filename = "results_fig03.png",
plot = wrlg_int_type_multipanel,
width = 7,
height = 4,
dpi = 600,
bg = "white",
path= "H:/nonwear_detection/preprint_figures/results/fig03")
```