我试图从使用R的大型数据集中提取某些数据。数据来自一个经历了几个阶段的过程,比如阶段0到5.我有一个包含多个过程运行的数据集。
我正在尝试为每次运行提取数据。我想创建第一次出现阶段0到5的子集,然后再创建另一个子集,再次出现阶段0到5(第二次运行)。数据集仅按时间顺序包含过程数据和阶段编号,它不会告诉它在哪个运行中。但是,阶段按顺序排列,因此阶段列从0到5再从0开始。
我已经尝试使用一些while和for循环来组织数据,但是在如此大的数据集(700 000个条目)上这非常慢。即使只使用一小部分只有10000个条目,也需要很长时间。
数据集可能看起来像这样(第二列是阶段):
01, 0, 2, 4, 5, 3, 4,
02, 0, 3, 4, 5, 2, 2,
03, 0, 4, 5, 4, 9, 8,
04, 1, 8, 9, 2, 7, 3,
05, 1, 8, 7, 0, 7, 8,
06, 2, 8, 4, 9, 7, 8,
07, 2, 9, 7, 5, 0, 8,
08, 2, 8, 6, 5, 7, 9,
07, 2, 8, 7, 6, 7, 9,
08, 3, 7, 8, 6, 7, 9,
09, 3, 7, 9, 8, 7, 8,
10, 4, 5, 6, 7, 4, 3,
11, 4, 6, 7, 5, 6, 4,
12, 5, 6, 4, 3, 2, 2,
13, 0, 6, 3, 3, 2, 5,
14, 0, 5, 6, 3, 2, 2,
15, 1, 5, 2, 1, 4, 4,
请注意,每相的行数不是恒定的。
我从上面的例子中期望的子集将是:
01, 0, 2, 4, 5, 3, 4,
02, 0, 3, 4, 5, 2, 2,
03, 0, 4, 5, 4, 9, 8,
04, 1, 8, 9, 2, 7, 3,
05, 1, 8, 7, 0, 7, 8,
06, 2, 8, 4, 9, 7, 8,
07, 2, 9, 7, 5, 0, 8,
08, 2, 8, 6, 5, 7, 9,
07, 2, 8, 7, 6, 7, 9,
08, 3, 7, 8, 6, 7, 9,
09, 3, 7, 9, 8, 7, 8,
10, 4, 5, 6, 7, 4, 3,
11, 4, 6, 7, 5, 6, 4,
12, 5, 6, 4, 3, 2, 2,
和
13, 0, 6, 3, 3, 2, 5,
14, 0, 5, 6, 3, 2, 2,
15, 1, 5, 2, 1, 4, 4,
(注意:在原始数据集中,阶段总是以5结束)
这是一个矢量化解决方案
# sample df
df = read.table(text = "
v1 phase v3 v4 v5 v6 v7
01 0 2 4 5 3 4
02 0 3 4 5 2 2
03 0 4 5 4 9 8
04 1 8 9 2 7 3
05 1 8 7 0 7 8
06 2 8 4 9 7 8
07 2 9 7 5 0 8
08 2 8 6 5 7 9
07 2 8 7 6 7 9
08 3 7 8 6 7 9
09 3 7 9 8 7 8
10 4 5 6 7 4 3
11 4 6 7 5 6 4
12 5 6 4 3 2 2
13 0 6 3 3 2 5
14 0 5 6 3 2 2
15 1 5 2 1 4 4
", stringsAsFactors = F, header = T)
# create groups for splitting df
grp <- cumsum( c( 0, diff( df$phase ) ) < 0 ) + 1
# split the df
split(df, grp)
#> $`1`
#> v1 phase v3 v4 v5 v6 v7
#> 1 1 0 2 4 5 3 4
#> 2 2 0 3 4 5 2 2
#> 3 3 0 4 5 4 9 8
#> 4 4 1 8 9 2 7 3
#> 5 5 1 8 7 0 7 8
#> 6 6 2 8 4 9 7 8
#> 7 7 2 9 7 5 0 8
#> 8 8 2 8 6 5 7 9
#> 9 7 2 8 7 6 7 9
#> 10 8 3 7 8 6 7 9
#> 11 9 3 7 9 8 7 8
#> 12 10 4 5 6 7 4 3
#> 13 11 4 6 7 5 6 4
#> 14 12 5 6 4 3 2 2
#>
#> $`2`
#> v1 phase v3 v4 v5 v6 v7
#> 15 13 0 6 3 3 2 5
#> 16 14 0 5 6 3 2 2
#> 17 15 1 5 2 1 4 4
由reprex package创建于2019-04-15(v0.2.1)
说明:diff
计算特定值与前一个值的差值,然后<
运算符计算结果是否小于0
。得到的T, F
逻辑向量在前面附加另一个0
,以保持向量长度与df
中的行数相同。然后cumsum
找到累积运行的值之和,这只会在第5阶段之后存在较小的相位值时发生变化,因此基本上会创建组。然后这些组(grp
)用于分裂df
。
在您的问题中,只有相位索引(在第二列中给出)减少时才会启动新子集。因此,我们可以将which
与diff
结合起来得到一个阶段结束的行索引。
我们首先可以获得子集开始/结束的行索引,如下所示:
end <- which(diff(data[,2]) < 0)
start <- c(1, end + 1)
end <- c(end, nrow(data))
然后我们可以使用它来制作包含子集的列表,如下所示:
subsets <- vector("list", length(start))
for (idx in 1:length(start))
subsets[[idx]] <- data[start[idx]:end[idx],]
请注意,最后一步仍然使用for循环。我希望split
可以实现类似的功能,但我没有成功尝试。
编辑:当然R有一个避免for-statement的解决方案。我们应该创建一个新的向量,指示每个行所属的子集。鉴于以上两个向量end
和start
,这很容易实现(参见下面的代码片段)。然后我们可以使用内置函数split
并将其输出转换为矩阵列表(灵感来自this answer)。
# How many rows does each subset has?
n <- end - start + 1
# Create vector that indicates for each row to which subset it belongs
idx_subset <- rep(1:length(start), n)
# Create the subsets
subsets <- lapply( split(data, idx_subset), matrix, ncol=7)