This function applies a Savitzky-Golay filter to smooth movement data while preserving higher moments (peaks, valleys) better than moving average filters. The implementation uses zero-phase filtering to prevent temporal shifts in the data.
Usage
filter_sgolay(
x,
sampling_rate,
window_size = ceiling(sampling_rate/10) * 2 + 1,
order = 3,
preserve_edges = FALSE,
na_action = "linear",
keep_na = FALSE,
...
)
Arguments
- x
Numeric vector containing the movement data to be filtered
- sampling_rate
Sampling rate of the data in Hz. Must match your data collection rate (e.g., 60 for 60 FPS motion capture).
- window_size
Window size in samples (must be odd). Controls the amount of smoothing. Larger windows give more smoothing but may over-attenuate genuine movement features. Default is automatically calculated as sampling_rate/10 (rounded up to nearest odd number).
- order
Polynomial order (default = 3). Controls how well the filter preserves higher-order moments in the data: - order=2: Preserves position, velocity (good for smooth movements) - order=3: Also preserves acceleration (good for most movement data) - order=4: Also preserves jerk (good for quick movements) - order=5: Maximum preservation (may retain too much noise)
- preserve_edges
Logical indicating whether to use progressively smaller windows at the beginning and end of the signal to reduce edge effects (default = FALSE). Note: This only affects the signal endpoints, not internal discontinuities.
- na_action
Method to handle NA values before filtering. One of: - "linear": Linear interpolation (default) - "spline": Spline interpolation for smoother curves - "locf": Last observation carried forward - "value": Replace with a constant value - "error": Raise an error if NAs are present
- keep_na
Logical indicating whether to restore NAs to their original positions after filtering (default = FALSE)
- ...
Additional arguments passed to replace_na()
Details
The Savitzky-Golay filter fits successive polynomials to sliding windows of the data. This approach preserves higher moments of the data better than simple moving averages or Butterworth filters, making it particularly suitable for movement data where preserving features like peaks and valleys is important.
Edge Handling: When preserve_edges = TRUE, the function uses progressively smaller windows near the beginning and end of the signal to reduce endpoint distortion. This only affects the signal endpoints - it does not detect or handle internal discontinuities or sharp events within the data.
Parameter Selection Guidelines:
window_size:
For 60 FPS: 5-15 frames (83-250ms) for quick movements, 15-31 for slow movements
For 120 FPS: 7-21 frames (58-175ms) for quick movements, 21-51 for slow movements
For 500 FPS: 25-75 frames (50-150ms) for quick movements, 75-151 for slow movements The default window_size = sampling_rate/10 works well for typical human movement.
order:
order=2: Smooth movements, position analysis
order=3: Most movement analysis (default)
order=4: Quick movements, sports analysis
order=5: Very quick movements, impact analysis Note: order must be less than window_size
Common values by application:
Gait analysis (60 FPS): window_size=15, order=3
Sports biomechanics (120 FPS): window_size=21, order=4
Impact analysis (500 FPS): window_size=51, order=4
Posture analysis (60 FPS): window_size=31, order=2
References
Savitzky, A., & Golay, M.J.E. (1964). Smoothing and Differentiation of Data by Simplified Least Squares Procedures. Analytical Chemistry, 36(8), 1627-1639.
See also
filter_lowpass
for frequency-based filtering
sgolayfilt
for the base Savitzky-Golay implementation
replace_na
for details on NA handling methods
Examples
# Generate example movement data: smooth motion + noise
t <- seq(0, 5, by = 1/60) # 60 FPS data
x <- sin(2*pi*0.5*t) + rnorm(length(t), 0, 0.1)
# Basic filtering with default parameters (60 FPS)
filtered <- filter_sgolay(x, sampling_rate = 60)
# Adjusting parameters for quick movements
filtered_quick <- filter_sgolay(x, sampling_rate = 60,
window_size = 11, order = 4)
# High-speed camera data (500 FPS) with larger window
filtered_high <- filter_sgolay(x, sampling_rate = 500,
window_size = 51, order = 3)