这种从长到宽的重塑我做错了什么?

时间:2014-05-19 09:49:34

标签: r machine-learning data.table reshape

问题

我编写的一个函数,用于扩展重复多变量时间序列数据的长表,用于输入分类器函数,即使对于简单的测试数据,也会导致错误的结果,但我无法找到问题。

背景

对于大多数R成语的速度和易用性,我在这样的长数据表格格式中保留了一系列多变量时间序列的重复试验:

> this.data
              Time Trial Class Channel      Value
     1: -100.00000     1    -1      V1  0.4551513
     2:  -96.07843     2    -1      V1  0.8241555
     3:  -92.15686     3    -1      V1  0.7667328
     4:  -88.23529     4    -1      V1  0.7475106
     5:  -84.31373     5    -1      V1  0.9810273
    ---                                          
204796:  884.31373   196     1      V4 50.2642220
204797:  888.23529   197     1      V4 50.5747661
204798:  892.15686   198     1      V4 50.5749421
204799:  896.07843   199     1      V4 50.1988299
204800:  900.00000   200     1      V4 50.7756015

具体而言,上述数据的Time列包含从0到900的256个唯一数字,每个Channel重复一次Trial。同样,对于每个Channel,每个V1,V2,V3,V4Time中的一个,对每个Trial样本重复。换句话说,Time,Trial,Channel的任意组合都唯一地指定Value。为了简单起见,100以下的所有Trial都有Class -1,而99以上的所有Class都有Value 1.(出于测试目的,所有Class都在Class 1的平均值为50,而dummy.plug() 0的平均值为0.(可以使用a gist I made中包含的stepclass函数生成和调整此数据。)

为了使用不同的机器学习分类算法处理数据,似乎有必要将数据重新整形为更宽一些,以便每个时间序列都有自己的列,而其他时间序列仍然是ID 。 (例如,来自klaR的逐步分类器cast需要不同列中的要素,因此它可以选择在训练时丢弃或添加到其模型中的那些。)由于有重复试验,我没有成功使现有的功能像##### converting from long table form to channel-split wide form ##### # for multivariate repeated time series channel.form <- function(input.table, value.col = "Voltage", split.col = "Channel", class.col = "Class", time.col = "Time", trial.col = "Trial") { # Converts long table format to slightly wider format split by channels. # For epoched datasets. setkeyv(input.table, class.col) chan.split <- split(input.table,input.table[,get(split.col)]) chan.d <- cbind(lapply(chan.split, function(x){ x[,value.col,with=FALSE]})) chan.d <- as.data.table(matrix(unlist(chan.d), ncol = input.table[,length(unique(get(split.col)))], byrow=TRUE)) # reintroduce class labels # since the split is over identical sections for each channel, we can just use # the first split's labels chan.d <- chan.d[,c(class.col):= chan.split[[1]][,get(class.col)]] chan.d[,c(class.col):=as.factor(get(class.col))] # similarly with time and trial labels chan.d <- chan.d[,Time:= chan.split[[1]][,get(time.col)]] chan.d <- chan.d[,Trial:= chan.split[[1]][,get(trial.col)]] return(chan.d) } 家庭工作,所以我写了自己的:

data.table

使用此功能,我将一些多变量试验(我已经准备好了一个长的> this.data.training.channel V1 V2 V3 V4 Class Time Trial 1: -50.58389 -50.56397 -50.74251 -50.86700 -1 -100.00000 1 2: -50.92713 -50.28009 -50.15078 -50.70161 -1 -96.07843 2 3: -50.84276 -50.02456 -50.20015 -50.45228 -1 -76.47059 7 4: -50.68679 -50.05475 -50.04270 -50.83900 -1 -72.54902 8 5: -50.55954 -50.88998 -50.01273 -50.86856 -1 -68.62745 9 --- 35836: 49.52361 49.37465 49.73997 49.10543 1 876.47059 194 35837: 49.93162 49.38352 49.62406 49.16854 1 888.23529 197 35838: 49.67510 49.63853 49.54259 49.81198 1 892.15686 198 35839: 49.26295 49.98449 49.60437 49.03918 1 896.07843 199 35840: 49.05030 49.42035 49.48546 49.73438 1 900.00000 200 像顶部的那个一样,并将它们重新塑造成更宽的一个,看起来像这样:

lda()

此时,我将扩大的表格放到像lda.model <- lda(Class ~ . -Trial, this.data.training.channel) lda.pred <- predict(lda.model, this.data.testing.channel) 这样的分类器上,然后在相同数据的单独随机部分进行测试:

> table(predicted = lda.pred$class, data = this.data.testing.channel[,Class])
         data
predicted   -1    1
       -1 2119 1878
       1  5817 5546

> 1-sum(lda.pred$class != this.data.testing.channel[,Class])/length(lda.pred$class)
[1] 0.4984375

> table(predicted = sda.pred$class, data = this.data.testing.channel[,Class])
         data
predicted   -1    1
       -1 3705 3969
       1  3719 3967

> 1-sum(sda.pred$class != this.data.testing.channel[,Class])/length(sda.pred$class)
[1] 0.4994792

症状

然而,即使我生成了淫秽分离的虚拟数据(见下图),我现有合理的库也会获得近乎可能的结果。 (我知道库可能没有错,因为如果我允许算法使用试验索引作为训练功能,它会正确地对每个输入进行分类。)

hugely separated two-class data in four channels

1

错误率基本上是硬币翻转,尽管类-1的值大约是类Channel的值的50倍。我必须犯一些巨大的错误(我认为这是一个编程问题,否则我会在交叉验证上结束),但我花了几天时间来刺激它并重写代码而没有任何改进。 (例如,请注意,无论是否缩放输入值,我都会得到相同的结果,因此它们的均值为0,方差为1。)

重现问题

可以使用完整的要点来重现问题here

我考虑的可能问题,我尝试了什么

(由于篇幅考虑,请参阅完整列表的问题的先前修订版)

我编写了一个函数(包含在gist中)以生成易于分离的虚拟数据,并编写了另一个函数来平均这两个类中的每一个,由Class构成并由{{1}着色就像上面的情节一样。使用每个参数(总体均值,通道数等的差异)似乎产生预期输出,以及使用this.data[Trial==1,unique(Time),by=Subject]等调用在适当的子集上查看。

我需要解决这个问题?

我非常感谢有关解决这个问题的任何建议。我只是看不出我做错了什么。

如果有人诊断/找到了问题,或者能够使用不同的方法来说明使用这些(流行的)分类器功能的数据重新整形的表格,我不会接受,我会奖励赏金(当然,经过测试)。

会话信息

R version 3.0.2 (2013-09-25)
Platform: x86_64-pc-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] parallel  grid      stats     graphics  grDevices utils     datasets  methods  
[9] base     

other attached packages:
 [1] doMC_1.3.2              iterators_1.0.6         AUC_0.3.0              
 [4] LiblineaR_1.80-7        RcppRoll_0.1.0          RcppArmadillo_0.4.300.0
 [7] Rcpp_0.11.1             foreach_1.4.1           cvTools_0.3.2          
[10] robustbase_0.90-2       latticist_0.9-44        vcd_1.3-1              
[13] latticeExtra_0.6-26     lattice_0.20-29         pheatmap_0.7.7         
[16] RColorBrewer_1.0-5      klaR_0.6-10             MASS_7.3-29            
[19] ggplot2_0.9.3.1         reshape2_1.2.2          data.table_1.9.2       
[22] sda_1.3.3               fdrtool_1.2.12          corpcor_1.6.6          
[25] entropy_1.2.0           zoo_1.7-11              testthat_0.8           

loaded via a namespace (and not attached):
 [1] codetools_0.2-8  colorspace_1.2-4 combinat_0.0-8   compiler_3.0.2   DEoptimR_1.0-1  
 [6] dichromat_2.0-0  digest_0.6.4     gtable_0.1.2     gWidgets_0.0-52  labeling_0.2    
[11] munsell_0.4.2    plyr_1.8         proto_0.3-10     scales_0.2.3     stringr_0.6.2   
[16] tools_3.0.2   

2 个答案:

答案 0 :(得分:2)

我无法重现您的错误,我发现dummy.plug()存在一些问题。我使用

生成数据
library(data.table)
library(reshape2)
library("MASS")

set.seed(115)
pp<-dummy.plug(trial.count = 200,
    chan.count = 4,
    mean.diff = 100,
    value.name = "Value")

我不关心data.table所以我只是将它转换为基本的data.frame。

dd<-as.data.frame(pp)

现在你说TimeTrialChannel应该唯一地标识一个值,但虚拟数据中似乎并非如此。我明白了

subset(dd, Time==-100 & Trial==1 & Channel=="V1")

#       Time Trial Class Channel      Value
# 1     -100     1    -1      V1 0.73642916
# 6401  -100     1    -1      V1 0.17648939
# 12801 -100     1    -1      V1 0.41366964
# 19201 -100     1    -1      V1 0.07044473
# 25601 -100     1    -1      V1 0.86583284
# 32001 -100     1    -1      V1 0.24255411
# 38401 -100     1    -1      V1 0.92473225
# 44801 -100     1    -1      V1 0.69989600

因此每种组合显然有多个值。所以继续,我决定采取观察值的平均值。使用dcast

时我没有遇到任何问题
xx<-dcast(dd, Class+Time+Trial~Channel, fun.aggregate=mean)

然后我将训练/测试数据集分开

train.trials = sample(unique(dd$Trial), 140)
train.data = subset(xx, Trial %in% train.trials)
test.data = subset(xx, !Trial %in% train.trials)

然后我按上面的说法运行了lda

lda.model <- lda(Class ~ . -Trial, train.data)
lda.pred <- predict(lda.model, test.data)

我查看了我的表现

table(lda.pred$class, test.data$Class)
#        -1    1
#   -1  704    0
#   1     0 1216

而且我看起来比你做得好得多。

除非在将data.table转换为data.frame时发生了不好的事情,否则您的测试数据似乎存在问题。也许您的非转换重塑功能存在问题。看看dcast如何正常工作,也许您想要检查您的功能是否也能正常工作。

答案 1 :(得分:1)

MrFlick在这两方面都是正确的。为了完整起见,这里有一个data.table答案,并有一些额外的解释。

坏的虚拟数据函数

上述要点中的虚拟功能确实很糟糕;关键点是这些:

  dummy.data <- data.table(matrix(runif(length(time.vector)*trial.count*chan.count),
                                  ncol=chan.count),
                           Time=rep(time.vector,times = trial.count))
  setkey(dummy.data,Time)
  dummy.data <- dummy.data[,Trial:=seq_len(trial.count)]

由于Trial一旦设置将在表中循环使用,因此每隔一列必须匹配相同的排列(包裹Trial值)。一种快速的方法是按Time排序,这是setkey()的影响之一。完成此操作后,数据确实可以进行唯一排序:

# load dummy data
set.seed(115)
this.data <- dummy.plug(trial.count = 200,
                       chan.count = 4,
                       mean.diff = 50,
                       value.name = "Value")
> this.data[(Trial==1 & Channel=="V1" & Time == -100),]
   Time Trial Class Channel     Value
1: -100     1    -1      V1 0.7364292

dcast现在正在工作

现在满足唯一性标准,dcast适用于数据表:

> this.data.channel <- dcast.data.table(this.data,
+                                                Class+Time+Trial~Channel,
+                                                fun.aggregate=identity)
Using 'Value' as value column. Use 'value.var' to override
> this.data.channel
       Class Time Trial           V1         V2         V3          V4
    1:    -1 -100     1 7.364292e-01  0.8889176  0.4638730  0.61258621
    2:    -1 -100     2 9.030099e-02  0.1435559  0.1596734  0.88577669
    3:    -1 -100     3 6.685920e-01  0.1013146  0.7156151  0.51144831
    4:    -1 -100     4 9.154142e-04  0.2429634  0.3169072  0.05810808
    5:    -1 -100     5 7.383397e-01  0.3668977  0.3779892  0.34938949
   ---                                                                
51196:     1  900   196 5.028103e+01 50.2810276 50.2810276 50.28102761
51197:     1  900   197 5.080229e+01 50.8022872 50.8022872 50.80228716
51198:     1  900   198 5.084255e+01 50.8425466 50.8425466 50.84254662
51199:     1  900   199 5.096859e+01 50.9685913 50.9685913 50.96859133
51200:     1  900   200 5.034459e+01 50.3445878 50.3445878 50.34458784

您可以快速查看检查结果是否正常:

> this.data.channel[,unique(Trial),by=Class]
     Class  V1
  1:    -1   1
  2:    -1   2
  3:    -1   3
  4:    -1   4
  5:    -1   5
 ---          
196:     1 196
197:     1 197
198:     1 198
199:     1 199
200:     1 200

分类检查

其余的要点与MrFlick的片段一样有效。

> lda.model <- lda(Class ~ . -Trial, this.data.training.channel)
> lda.pred <- predict(lda.model, this.data.testing.channel)
> table(predicted = lda.pred$class, data = this.data.testing.channel[,Class])
         data
predicted   -1    1
       -1 5888    0
       1     0 9472
> 1-sum(lda.pred$class != this.data.testing.channel[,Class])/length(lda.pred$class)
[1] 1

为什么我以前无法让dcast工作,我将不得不深入研究旧版本。我怀疑一个排列问题(在导入而不是生成期间)就像上面所做的那样。