我编写的一个函数,用于扩展重复多变量时间序列数据的长表,用于输入分类器函数,即使对于简单的测试数据,也会导致错误的结果,但我无法找到问题。
对于大多数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,V4
是Time
中的一个,对每个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
然而,即使我生成了淫秽分离的虚拟数据(见下图),我现有合理的库也会获得近乎可能的结果。 (我知道库可能没有错,因为如果我允许算法使用试验索引作为训练功能,它会正确地对每个输入进行分类。)
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
答案 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)
现在你说Time
,Trial
和Channel
应该唯一地标识一个值,但虚拟数据中似乎并非如此。我明白了
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
适用于数据表:
> 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
工作,我将不得不深入研究旧版本。我怀疑一个排列问题(在导入而不是生成期间)就像上面所做的那样。