Suboptimal sensory reweighting in car sick individuals: a postural study

Author

Dida Merrick, Guerraz Michel, Barraud Pierre-Alain, Cian Corinne

Published

December 7, 2022

Environment initialization

Specifying the paths for the data and scripts directories :

The data directory hosts the .brute files generated by our posturography platform.

The first part of the name of each files (i.e. s046) corresponds to the identification number of each participant.

The second part of the name corresponds to the number of the trial. Respectively cd1, cd2 and cd3 for the first, second and third trials.

The first column of these files (temps) is the timestamp of each measure (every 10 ms during 180s).

The second column Consigne correspond to the angle the platform must output to maintain sway-reference.

The third column Angle corresponds of the current angle of the platform in space in the anteroposterior axis.

The four following columns (Capteur (Kg) (Nord Ouest), Capteur (Kg) (Nord Est), Capteur (Kg) (Sud Est), Capteur (Kg) (Sud Ouest)), account for the force sensed on each of the four cardinally placed force sensors in Kg.

From these forces, the last two columns were computed to represent the Barycenter in mm (respectively on the x and y axis) of the participant on the plate.

data_dir <- file.path(getwd(), "data")
scripts_dir <- file.path(getwd(), "scripts")

Source the load-packages script and load/install (if necessary) needed packages :

# Specify the packages needed for the project
pkgs <- c("tidyverse",
          "psych",
          "signal",
          "psd",
          "tools",
          "viridis",
          "rmarkdown",
          "writexl")

# Load the packages and install them if necessary
lpkg(pkgs)
tidyverse     psych    signal       psd     tools   viridis rmarkdown   writexl 
     TRUE      TRUE      TRUE      TRUE      TRUE      TRUE      TRUE      TRUE 

Data setup

Loading the questionnaire data and compute the car-sickness score.

subj_quest <- read_csv(file.path(data_dir, "subj_quest.csv"))
subj_quest <-
  subj_quest %>%
    mutate(CarSick = replace_na(CarMoun +
                                CarStraigh +
                                CarFront +
                                CarBack +
                                CarRead, 0)) 

Setting-up signal parameters :

# Sampling frequency (in Hz)
samp_freq <- 100

# Duration of each phase (static/sway-ref/static, in sec)
phase_dur <- 60

# Minimum and maximum frequencies of the spectral analysis
min_freq <- 0
max_freq <- 5

# FFT window duration (in sec)
window_dur <- 10

# Decimation ratio (not used since reasonable compute time)

dratio <- 1
# Rotation function for future plots
rotate <- function(x) t(apply(x, 2, rev))

# Normalization function for the spectra
sp_norm <- function(x) {
    x / max(x)
    }

# Number of samples in the analysis
nb_samp <- floor(3 * phase_dur * samp_freq)

# Width of the FFT analysis window
window_size <- round(window_dur * samp_freq)

# Window profile
window <- hamming(window_size)

# Initial and final sample indices
initial_idx <- max(c(
  1, floor(window_size * min_freq / samp_freq)))


final_idx <- floor(window_size * max_freq / samp_freq)

# Number of steps for FFT analysis
nb_step <- nb_samp - window_size

Initialization of the spectra storage :

# Temporary matrix for storing the spectra
spectra <- matrix(
  NA, nrow = nb_step/dratio, ncol = final_idx - initial_idx + 1)

# List for the storing of results
rspec <- list()
rsptat <- list()
datastat <- tibble(
    subject = character(),
    condition = character(),
    er1 = numeric(),
    er2 = numeric(),
    er3 = numeric(),
    er4 = numeric(),
    er5 = numeric(),
    er2_er1 = numeric(),
    er4_er3 = numeric(),
    carsick = numeric(),
    age = numeric(),
    sexe = character(),
    height = numeric(),
    weight = numeric(),
    validity = numeric())
    cond <- c("cd1", "cd2", "cd3")

Extracting participants weights from the COP files :

weightlist <- list()

 # Looping through the subjects brute data and extracting weights :
for (s in seq(1, nrow(subj_quest))) {
 subject <- subj_quest$Id[s]

    filename <- paste0(
      file_path_sans_ext(as.character(subject)),
                                            "-",
                                          "cd1",
                                        ".brute")

   weightdf <- read.table(file.path(data_dir, filename),
                          sep = "\t",
                          skip = 1, header = TRUE)
   weight <- weightdf[1,4]+
             weightdf[1,5]+
             weightdf[1,6]+
             weightdf[1,7]
   
   
   weightlist [subject] <- weight
 }

#Creating a dataframe with weights and subj Id
weightdf <-  as.data.frame(do.call(rbind,weightlist))
weightdf <- rownames_to_column(weightdf, var="Id")
colnames(weightdf)[1]="Id"
colnames(weightdf)[2]="weight"

subj_quest <- merge(subj_quest,weightdf,by="Id")

#Remove Invalid participans
subj_quest <- subj_quest[subj_quest$isvalid != 0,]

Calculating PSDs sequences (spectra)

Since the spectra for each trial and each participant is relatively long to compute, they are saved in a rspec.dat in the data directory. For future runs this file will be used, if present, instead of computing the spectra again.

# Check if the rspec is already available in the directory
#and if not, compute it
if (file.exists(file.path(data_dir, "rspec.dat"))) {
  load(file.path(data_dir, "rspec.dat"))

  print("rspec.dat already exists, loading it from save...",
        quote = F)
  } else {

    # Looping through the subjects
  for (s in seq(1, nrow(subj_quest)))
    { # Loop through the subjects :
   
    # Get the subject id
      subject <- subj_quest$Id[s]
    
    # Check the validity of each trial for this subject

      valid1 <- subj_quest$valid1[subj_quest$Id == subject] == 1    
      valid2 <- subj_quest$valid2[subj_quest$Id == subject] == 1
      valid3 <- subj_quest$valid3[subj_quest$Id == subject] == 1
      
    # Creating vector mask for validity
      vvec <- c(valid1, valid2, valid3)

    # Applying the mask to the trials vector   
      trials <- cond[!!vvec]

      for (j in seq(1, length(trials))) { # Loop through the trials :
  
  # Get the conditions

      trial <- trials[j]
      perct <- round(((s/nrow(subj_quest))*100),2)

      filename <- paste0(file_path_sans_ext(
        as.character(subject))
        , "-", trial,".brute")
        cat(paste0(
          "\r", " Computing the spectra for the file : ",
          filename,
          "  ( ",
          perct,
          "% finished )"))
  
  # Get the posturography data
      data <- read.table(file.path(data_dir, filename),
                        sep = "\t",
                        skip = 1, header = TRUE)
      
      
      data <- head(data, nb_samp)
      
      y <- data$Barycentre.y..mm.
  
  # Compute the spectra
      for (i in seq(1, nb_step/dratio)) {
          t <- i * dratio
          idx <- seq(t, t + window_size - 1)
          p <- psdcore(y [idx])
  
          spec <- 10 * log10(p$spec)
          spectra [i, ] <- spec [initial_idx : final_idx]
      }
  
  # Extract trial result in a list  
    rspec [[subject]] [[trial]] <- list(spectra = spectra)
}}

save (rspec, file = file.path (data_dir, "rspec.dat"))
}
[1] rspec.dat already exists, loading it from save...
cat("\r Done computing spectra ! \n Saved in data/rspec.dat")

 Done computing spectra ! 
 Saved in data/rspec.dat

Computing Energy Ratios

Setting the time and frequency bands of interest for the computing of energy ratios :

# Setting the two frequency bands
bf <- c(1:7) # get the columns corresponding
#to the 0.1 to 0.7 Hz range

hf <- c(7:13) # get the columns corresponding
#to the 0.7 to 1.3 Hz range

# Setting the five time windows
t1 <- c((5000/dratio):(6000/dratio)) # get the rows corresponding
#to the 50s to 60 sec range

t2 <- c((6000/dratio):(7000/dratio)) # get the rows corresponding
#to the 60s to 70 sec range

t3 <- c((11000/dratio):(12000/dratio)) # get the rows corresponding
#to the 110s to 120 sec range

t4 <- c((12000/dratio):(13000/dratio)) # get the rows corresponding
#to the 120s to 130 sec range

t5 <- c((16000/dratio):(17000/dratio)) # get the rows corresponding
#to the 160s to 170 sec range

Computing Energy ratios for each trials of each participants and extracting them from a list to make a single data frame :

# Looping over each condition for all subjects
for (s in names(rspec)){
    for ( c in names (rspec [[s]])) {      
            # Get low frequency band for each time window
      #(Peterka : 0.1 - 0.7 Hz)
            l1 <- rspec [[s]][[c]]$spectra[t1, bf]
            l2 <- rspec [[s]][[c]]$spectra[t2, bf]
            l3 <- rspec [[s]][[c]]$spectra[t3, bf]
            l4 <- rspec [[s]][[c]]$spectra[t4, bf]
            l5 <- rspec [[s]][[c]]$spectra[t5, bf]

            
            # Get high frequency band for each time window
      #(Peterka : 0.7 - 1.3 Hz)
            h1 <- rspec [[s]][[c]]$spectra[t1, hf]
            h2 <- rspec [[s]][[c]]$spectra[t2, hf]
            h3 <- rspec [[s]][[c]]$spectra[t3, hf]
            h4 <- rspec [[s]][[c]]$spectra[t4, hf]
            h5 <- rspec [[s]][[c]]$spectra[t5, hf]

            

            #Compute Energy Ratio
            er1=(sum(h1)/sum(l1))
            er2=(sum(h2)/sum(l2))
            er3=(sum(h3)/sum(l3)) 
            er4=(sum(h4)/sum(l4))
            er5=(sum(h5)/sum(l5))
            
            #Create a list with only these bands and energy ratios
            
            rsptat [[s]] [[c]]<- list(
              h1 = h1, # High frequencies over each time windows
              h2 = h2,
              h3 = h3,
              h4 = h4,
              h5 = h5,
              l1 = l1, # Low frequencies over each time windows
              l2 = l2,
              l3 = l3,
              l4 = l4,
              l5 = l5,
              er1 = er1, # Energy ratio over each time windows
              er2 = er2,
              er3 = er3,
              er4 = er4,
              er5 = er5,
              er2_er1 = er2-er1,
              er4_er3 = er4-er3,
              carsick = subj_quest$CarSick[subj_quest$Id == s],
              age = subj_quest$Age[subj_quest$Id == s],
              sexe = subj_quest$Sexe[subj_quest$Id == s],
              height = subj_quest$Height[subj_quest$Id == s],
              weight = subj_quest$weight[subj_quest$Id == s],
              validity = subj_quest$isvalid[subj_quest$Id == s])
            
            
    }}
for (s in names (rsptat))
    # Loop over vision conditions
    for (c in names (rsptat [[s]]))
        # Loop over SR conditions
        {
            
            
            datatemp <- data.frame(
              subject = s, 
              condition = c,
              er1 = rsptat [[s]] [[c]]$er1,
              er2 = rsptat [[s]] [[c]]$er2,
              er3 = rsptat [[s]] [[c]]$er3,
              er4 = rsptat [[s]] [[c]]$er4,
              er5 = rsptat [[s]] [[c]]$er5,
              er2_er1 = rsptat [[s]] [[c]]$er2_er1,
              er4_er3 = rsptat [[s]] [[c]]$er4_er3,
              carsick = rsptat [[s]] [[c]]$carsick,
              age = rsptat [[s]] [[c]]$age,
              sexe = rsptat [[s]] [[c]]$sexe,
              height = rsptat [[s]] [[c]]$height,
              weight = rsptat [[s]] [[c]]$weight,
              validity = rsptat [[s]] [[c]]$validity)

            
             datastat <- rbind(datastat,datatemp)            
        }

Reformatting the dataframe by averaging ERs for every trials succeeded by each participants :

datamean  <-  aggregate(list(
                           er1 = datastat$er1,
                           er2 = datastat$er2,
                           er3 = datastat$er3,
                           er4 = datastat$er4,
                           er5 = datastat$er5,
                           er2_er1 = datastat$er2_er1,
                           er4_er3 = datastat$er4_er3,
                           carsick = datastat$carsick,
                           age= datastat$age,
                           weight = datastat$weight,
                           validity = datastat$validity
                           ),
                        by = list(subject = datastat$subject),
                        FUN = mean)

names(subj_quest)[names(subj_quest) == 'Id'] <- "subject"
sexe <- subj_quest[,c("subject","Sexe")]

datamean <- merge(datamean,sexe,by="subject")

Car sickness quartiles computation

Computing quartiles values for the car sickness score and adding quartile categorization in the dataframe :

# Extracting q1 and q3 (quartiles) values for car sickness
q1 <- quantile(datamean$carsick, 0.25)
q3 <- quantile(datamean$carsick, 0.75)
datamean <- datamean %>% 
  mutate(carsick_quart = 
    ifelse(datamean$carsick < q1,-1,
    ifelse(datamean$carsick > q3, 1, 0)),
     .after = validity)     

Saving and displaying the final dataframe

write_xlsx(datamean,"datamean.xlsx")
paged_table(datamean)