Операция BY на data.table без агрегирования - PullRequest
0 голосов
/ 27 февраля 2020

У меня есть проблема, которой я хотел бы поделиться из-за времени и проблем, которые потребовались мне для решения. У меня есть 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')

Generated and Filtered signal

Генерация данных 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()

enter image description here

Теперь основная проблема:

Я хотел бы пропустить сигнал моих данных непосредственно из данных стол без использования для петель. В действительности у меня есть около 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()

enter image description here

1 Ответ

0 голосов
/ 27 февраля 2020

Без изменения исходного набора данных и сохранения временного измерения:

filtered_df = df[,.(val=filtfilt(b,val),t=t) by=.(participant,factor1,factor2)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...