Table of Content

  1. Code
  2. Ingestion and Indexing
  3. Butterworth Filter
  4. Instantaneous Equilibrium Position
  5. Rambling
  6. Trembling
  7. Output Writing
  8. Documentation

\(\newcommand{\eq}[1]{\begin{align*}#1\end{align*}}\)

Code

###################################################### INGESTION ################################################ 
      # Folder with files to be analyzed
      source("functions.R") # load functions stored in file functions.R
      root = "/Users/jheintz/Box/02_Data_Analytics_Projects/000_KIN_PhD/02_Research/2020_Spring/BDS/" # original data sets
      rt = "/Users/jheintz/Box/02_Data_Analytics_Projects/000_KIN_PhD/02_Research/2020_Spring/00_PreProcessed/" # pre-processed data sets
      
      # Trial files  files in the folder
      all.files = list.files(root)
      all.files = all.files[!grepl("info", all.files)]
      amount.files = length(all.files)
      
 for (fls in 1:1){
      #for (fls in 1:amount.files){
            file = all.files[fls]
            f = paste0(root, file)
            df = read.delim(f)

# Indexing and keeping original for comparison's
      df = cbind(Index = 1:dim(df)[1], df)
      df.origin = df

# vector with variable names
      var.names = names(df)

     
       
#################################################### FILTER #################################################### 
# butterworth low pass filter
      o = 4       # filter order
      co = 10     # cut-off frequency
      tp = "low"  # filter type, low pass
      fs = 100    # sampling rate 100Hz

      fltrd.dt = apply( df[,-c(1,2)],
                        2, function(x)
                              butter.fltr(x,
                                    order = o,
                                    cut.freq = co,
                                    tp = tp,
                                    fs = fs))
# merging to data frame
      df = data.frame(df[,c(1,2)], fltrd.dt)

      
      
##################################################### IEP ####################################################### 
# IEP identification, "
      df$iep.x =  f.iep(x = df$Fx.N.) # x direction
      df$iep.y =  f.iep(x = df$Fy.N.) # y direction

# Counting IEPs
      iep.count = f.number.of.IEP(data = df, 
                                    fl = file,
                                    F_hor.1 = "iep.x", 
                                    F_hor.2 = "iep.y")

# Index for first and last IEP for subsetting
      fi.la.iep.index.x = fivenum(df[!is.na(df$iep.x) & df$iep.x == 1,]$Index)[c(1,5)]
      fi.la.iep.index.y = fivenum(df[!is.na(df$iep.y) & df$iep.y == 1,]$Index)[c(1,5)]

# Output IEP | Included in Subject and Trial Overview Table ()
     df.IEP = data.frame(
                 Trial.id = iep.count[,1],
                 IEPx = iep.count[,2], 
                 Fx.N.mean = mean(df$Fx.N.),
                 Index.min.x = fi.la.iep.index.x[1],
                 Index.max.x = fi.la.iep.index.x[2],
                 IEPy = iep.count[,3],
                 Fy.N.mean = mean(df$Fy.N.),
                 Index.min.y = fi.la.iep.index.y[1],
                 Index.max.y = fi.la.iep.index.y[2]
                 )

     

####################################### RAMBLING + SPLINE + Trembling #########################################
    
# AP(x) rambling, spine, trembling 
      if (df.IEP$IEPx != 0) {
            df$ramb.x = df$iep.x * df$COPx.cm.
            df$ramb.x = f.spline(x = df$Time.s., y = df$ramb.x, mtd = 'fmm') # mtd = fmm, natural , periodic
            df$tremb.x = df$COPx.cm. - df$ramb.x
      } else {
            df$ramb.x = "NO.IEP"
            df$tremb.x = "NO.IEP"
      }
     
 

      
# ML(y) rambling, spine, trembling 
      if (df.IEP$IEPy != 0) {
            df$ramb.y = f.spline(x = df$Time.s., y = df$ramb.y, mtd = 'fmm')
            df$ramb.y = df$iep.y * df$COPy.cm.
            df$tremb.y = df$COPy.cm. - df$ramb.y
      } else {
            df$ramb.y = "NO.IEP"
            df$tremb.y = "NO.IEP"
      }
      
      
      
################################################### Output Writing ##############################################
### Subject Trial Data Preprocessed, stored in csv file, 1 csv file per trial ###
      dt = as.character(Sys.Date())
      file = paste0(rt, dt, "_", df.IEP$Trial.id, "_", co, "Hz",  ".csv")
      
      write.csv(df, file = file, row.names = FALSE)

### Store Features in BDSinfo ### 
      ## read in file 
      file = "BDSinfo.csv"
      f = paste0(rt, file)
      df.info = read.csv(f)
      
      # store IEP statistics in BDSinfo
      Trial.id = iep.count$Trial.ID # taken from counting IEP points
      df.info[df.info$Trial == Trial.id, c("IEPx", "Fx.N.mean", "Index.min.x", 'Index.max.x', 
                                           "IEPy", "Fy.N.mean", "Index.min.y", "Index.max.y")] = 
                                           df.IEP[,c("IEPx", "Fx.N.mean", "Index.min.x", 'Index.max.x', 
                                                    "IEPy", "Fy.N.mean", "Index.min.y", "Index.max.y") ]
      
      write.csv(df.info, file = f, row.names = FALSE)
}

Ingestion and Indexing

Ingestion and indexing includes defining paths to force measurement raw data (root) and define storage path to store pre-processed data (rt). The source function loads defined functions stored in a separate file ‘functions.R’ to keep pipeline clean.
Input: root, rt, function.R file stored in same path as R script
Output: none
Functions: none
Variables: Index

Butterworth Filter

A 4 order low pass Butterworth filter is applied on COPx.cm., COPy.cm., Fx.N. ,Fy.N., Fz.N., Mx.Nm, My.Nm., and Mz.Nm.
Input: cut-off frequency, default co = 5Hz; order of low pass filter, default o = 4; sampling frequency, default fs = 100Hz; filter type, default tp = “low”
Output: filtered raw data
Functions: butter.fltr
Variables: none

Instantenous Equilibrium Position

Instantaneous equilibrium position (IEP) is assumed when horizontal forces are zero. The IEPs are identified for AP (anterior posterior and ML (medial lateral) for \(F_x = 0, F_y = 0\).
Input: raw data set with horizontal forces
Output: IEPs, min-max index for first and last IEP per trial.
Functions: The function “f.iep” identifies the IEP and provides a vector with “1” for IEP, and NA for everything else. The IEP vector becomes part of the raw data set for each trial. The function f.number.of.IEP provides the index for the first, the last IEP, number of IEPs for each trial, and mean of Fx, and Fy. The variables are part of the BDSinfo file.
Variables: Trial.ID = Trial ID, IEPx = Nummber of identified IEPs for AP, Fx.N.mean = mean of horizontal force Fx, Index.min.x = Index first IEPx (AP), Index.max.x = Index Last IEPx (AP), IEPy = Nummber of identified IEPs for ML, Index.min.y = Index first IEPy (ML), Index.max.y = Index Last IEPy (ML)

Example BDSInfo
Trial.id IEPx Fx.N.mean Index.min.x Index.max.x IEPy Fy.N.mean Index.min.y Index.max.y
BDS00001 0 -1.731919 NA NA 0 -3.810546 NA NA

IEPx = 0 means the horizontal forces did not cross the zero line. The mean provides a sense of how much subject in that trial has applied horizontal force on average.


Rambling

The rambling trajectory is determined by 3 steps. First, identifying IEP’s; IEP \(F_{hor} = 0\) for ML and AP. Second, identifying the values of COPx and COPy at the IEPs. Third, interpolating the values to a curve. For the entire interpolated trajectory it is assumed that \(F_{hor} = 0\) and the subject is in instantaneous equilibrium.
Input: raw data set with horizontal forces; interpolation method (splining) default mtd = ffm
Output: Rambling trajectory
Functions: f.spline
Variables: ramb.x = rambling AP[cm], ramb.y = rambling ML [cm]


Trembling

The Trembling trajectory is determined by subtracting the Rambling trajectories (ML, AP) from the COPx, COPy trajectories. It describes how a subject migrates his COP around the Rambling trajectory.
Input: raw dat
Output: Trembling trajectory
Functions: none
Variables: tremb.x = trembling AP [cm], tremb.y = trembling ML [cm]


Output Writing

The data generated and processed are stored in BDSInfor file. The BDSInfo file provides contextual information about the subject and trial. All preprocessed trial data are stored in the

Introduced variables

IEP
- Trial ID
- IEPx = # IEPs for AP
- Fx.N.mean
- IEPy = # IEPs for ML
- Fy.N.mean
- Index.min.x = Index first IEPx (AP)
- Index.min.y = Index first IEPy (ML)
- Index.max.x = Index Last IEPx (AP)
- Index.max.y = Index Last IEPy (ML)
Rambling
- ramb.x = rambling AP [cm]
- ramb.y = rambling ML [cm]
Trembling
- tremb.x = trembling AP [cm]
- tremb.y = trembling ML [cm]


References

  1. Perperoglou, A., Sauerbrei, W., Abrahamowicz, M. et al. A review of spline function procedures in R. BMC Med Res Methodol 19, 46 (2019). https://doi.org/10.1186/s12874-019-0666-3
  2. Zatsiorsky, V. M., & Duarte, M. (1999). Instant equilibrium point and its migration in standing tasks: rambling and trembling components of the stabilogram. In Motor control (Vol. 3, Issue 1, pp. 28–38). https://doi.org/10.1123/mcj.3.1.28
  3. Zatsiorsky, V. M., & Duarte, M. (2000). Rambling and trembling in quiet standing. Motor Control, 4(2), 185–200. https://doi.org/10.1123/mcj.4.2.185
  4. Santos, D. A., & Duarte, M. (2016). A public data set of human balance evaluations. PeerJ, 2016(11). https://doi.org/10.7717/peerj.2648
  5. Prieto, T. E., Myklebust, J. B., Hoffmann, R. G., Lovett, E. G., & Myklebust, B. M. (1996). Measures of postural steadiness: Differences between healthy young and elderly adults. IEEE Transactions on Biomedical Engineering, 43(9), 956–966. https://doi.org/10.1109/10.532130