У меня есть проблема, которой я хотел бы поделиться из-за времени и проблем, которые потребовались мне для решения. У меня есть data.table, полный шумного сигнала ЭЭГ, который я хочу пропустить перед построением графика. Я поместил все сигналы участников в таблицу данных R. вместе с их многочисленными факторами.
Мой набор данных имеет
- x = время
- y = значение
- имя участника = p_name
- factor1 и factor 2
Проблема в том, что я хотел бы напрямую go из data.table в ggplot2 без необходимости использовать 6 imbricated цикл for для пропускания всех данных при любых условиях.
Я представляю здесь решение, которое заключается в «забывании» фактора / измерения для анализа. Таким образом, вместо классического синтаксиса:
DF[, m:=mean(value),
by=.(p_name,factor1,factor2,time))
Выделение временного фактора из by
позволяет выполнять операции со всеми значениями во времени (в моем случае частотный фильтр), возвращая список, непосредственно примененный к каждому значение.
# myfunc(x) returns x filtered for high frequencies
DF[, value_filt:=my_func(value),
by=.(p_name,factor1,factor2)) # <- time not in the list
Интересный факт, фактор времени даже не теряется в процессе.
Существует ли лучшее / более быстрое решение?
Код для репликации
library(data.table)
library(signal)
library(ggplot2)
# Operations on data.table without aggregation
set.seed(1234)
fs = 128 # Hz sampling rate (=length of 1 sec vector)
tseq <- seq(0, .999, by = 1/fs) # t = 128 samples for 1 second
# For generating signal 128 I created a function
# that mixes together two sin waves + noise
generate_sig = function(t) {
x <- sin(rnorm(1)*40*pi*t*.5) + 0.11*rnorm(length(t)) + sin(rnorm(1)*40*pi*t*.5) + 0.31*rnorm(length(t)) # create two random sinusoid+noise
return(x)
}
# Testing the function
x = generate_sig(tseq)
plot(NA,NA,xlim=c(0,128),ylim=c(-pi,pi),xlab='t',ylab='signal ampitude')
lines(x,col='red')
# Generating a Butterworth filter
b = butter(2,c(1,15)*(2/fs))
# Applying the filter
xfil = filtfilt(b,x)
# Plotting
lines(xfil,col='black')
Генерация данных data.table
в длинной таблице с сигналом для двух участников, оба с коэффициентом 1 и условия factor2
val_pname=c('p1', 'p2')
val_factor1=c('left','right')
val_factor2=c('pain', 'reward', 'sham')
nb_samples = length(tseq)
col_pname = factor(rep(c(val_pname),each=length(val_factor1)*length(val_factor2)*nb_samples))
col_factor1 = factor(rep(rep(c(val_factor1),each=length(val_factor2)*nb_samples),length(val_pname)))
col_factor2 = factor(rep(rep(rep(c(val_factor2),each=nb_samples),length(val_factor1)),length(val_pname)))
col_t= rep(rep(rep(tseq,length(val_factor2)),length(val_factor1)),length(val_pname))
col_values = replicate(length(val_factor2)*length(val_factor1)*length(val_pname),generate_sig(tseq))
col_values = as.numeric(as.list(col_values))
df = data.table(participant=col_pname,factor1=col_factor1,factor2=col_factor2,t=col_t,t_idx=col_t_idx,val=col_values)
# visualizing the whole data table
ggplot(df,aes(x=t, y=val, color=factor1))+
geom_line()+
facet_grid(factor2~participant)+
theme_bw()
Теперь основная проблема:
Я хотел бы пропустить сигнал моих данных непосредственно из данных стол без использования для петель. В действительности у меня есть около 6 различных факторов, и я хочу иметь возможность агрегировать их (в среднем), как мне угодно, прежде чем выполнять полосовую фильтрацию
**** SOLUTION ****
# I managed to work it out this way
df[,val2:=filtfilt(b,val), by=.(participant,factor1,factor2)]
Агрегирование по фактору времени t не производится, поэтому размер входной таблицы совпадает с размером выходной таблицы.
Визуализация решения
# visualizing the filtered data table
ggplot(df,aes(x=t, y=val2, color=factor1))+
geom_line()+
facet_grid(factor2~participant)+
theme_bw()