在数据框中按周期对数据进行分组

4
我已经创建了一个简化版的数据:
a <- data.frame(a = c(0,1,2,3,2,1,0,-1,-2,-3,-2,-1,
                        0,1,2,3,4,3,2,1,0,-1,-2,-3,-2,-1,
                        0,1,2,1,0,-1,-2,-3,-2,-1,0,1,2,3,2,1,
                        0,-1,-2,-3,-2,-1,0,1,2,3,2,1,
                        0,-1,-2,-3,-4,-3,-2,-1,0))
a$b <- seq(1,length(a$a),1)

我试图检测数据中的循环,具体方法如下(欢迎更好的建议):

library(quantmod)
max <- findPeaks(a$a)
min <- findValleys(a$a)

这实际上是在最大值和最小值之后得到的点。我想找到每个周期的跨度和设置点。

周期: - 第一个周期定义为第一个设定点之前的第一个数据点。例如,考虑第一个设定点为-1.0,第一个周期在行1到12定义。选择行12作为周期的结束,因为它出现在第一个峰谷之后,而且也小于或等于-1.0。第二个周期从第13行开始,到第27行结束,因为第27行的幅度为0,小于或等于0.5,并且出现在第二个峰谷点之后。:

span <- a[max-1,]$a-a[min-1,]$a
set <-  a[max,]$a - span/2

我希望对原始数据框 a 进行分组,并将每个周期(组)的循环号、跨度和设定点分配给相应的行组。
期望的输出如下:
> print(a)
    a  b cycles span   set
1   0  1      1    6 -1.0
2   1  2      1    6 -1.0
3   2  3      1    6 -1.0
4   3  4      1    6 -1.0
5   2  5      1    6 -1.0
6   1  6      1    6 -1.0
7   0  7      1    6 -1.0
8  -1  8      1    6 -1.0
9  -2  9      1    6 -1.0
10 -3 10      1    6 -1.0
11 -2 11      1    6 -1.0
12 -1 12      1    6 -1.0
13  0 13      2    7  0.5
14  1 14      2    7  0.5
15  2 15      2    7  0.5
16  3 16      2    7  0.5
17  4 17      2    7  0.5
18  3 18      2    7  0.5
19  2 19      2    7  0.5
20  1 20      2    7  0.5
21  0 21      2    7  0.5
22 -1 22      2    7  0.5
23 -2 23      2    7  0.5
24 -3 24      2    7  0.5
25 -2 25      2    7  0.5
26 -1 26      2    7  0.5
27  0 27      2    7  0.5
28  1 28      3    5 -1.5
29  2 29      3    5 -1.5
30  1 30      3    5 -1.5
31  0 31      3    5 -1.5
32 -1 32      3    5 -1.5
33 -2 33      3    5 -1.5
34 -3 34      3    5 -1.5
35 -2 35      3    5 -1.5
36 -1 36      4    6 -1.0
37  0 37      4    6 -1.0
38  1 38      4    6 -1.0
39  2 39      4    6 -1.0
40  3 40      4    6 -1.0
41  2 41      4    6 -1.0
42  1 42      4    6 -1.0
43  0 43      4    6 -1.0
44 -1 44      4    6 -1.0
45 -2 45      4    6 -1.0
46 -3 46      4    6 -1.0
47 -2 47      4    6 -1.0
48 -1 48      4    6 -1.0
49  0 49      5    7 -1.5
50  1 50      5    7 -1.5
51  2 51      5    7 -1.5
52  3 52      5    7 -1.5
53  2 53      5    7 -1.5
54  1 54      5    7 -1.5
55  0 55      5    7 -1.5
56 -1 56      5    7 -1.5
57 -2 57      5    7 -1.5
58 -3 58      5    7 -1.5
59 -4 59      5    7 -1.5
60 -3 60      5    7 -1.5
61 -2 61      5    7 -1.5
62 -1 62      5    7 -1.5
63  0 63      5    7 -1.5

分配 spanset 的逻辑是什么?第一个值如何获得行1-12,第二个值获得13-27等等? - Ronak Shah
@RonakShah span 给出了每个周期的范围,因此我在每个周期中获取最大值和平均值以找到其范围。set 是周期振荡的点,因此如果它完全对称,则应为零,但由于我的数据不对称,我将每个周期的最大值减去其跨度的一半。 - Maral Dorri
2
好的,但是我仍然不清楚你是如何决定第一个周期从1到12的。 - Ronak Shah
我希望代码能够理解的定义(我是如何决定每个周期所属数据点的)是:1. 找到峰值和谷值。2. 计算每个峰和谷之间的跨度和设定点。3. 将所有在设定点之前的数据点分组为一个周期。这意味着从开始到第一个设定点将成为第一个周期。现在我明白,设定点是一个平均值,可能不存在于数据集中。因此,在代码中需要选择小于或等于设定点的点。这有意义吗?@RonakShah - Maral Dorri
但问题仍然存在,循环首先如何定义? - AnilGoyal
@AnilGoyal 我所拥有的数据是实验性的,因此我已经过滤掉了不需要的数据。第一个周期被定义为第一个设定点之前的第一个数据点。例如,考虑第一个设定点为-1.0,则第一个周期在行1到12定义。选择第12行作为周期的结束,因为它出现在第一个峰值和谷值之后,并且小于或等于-1.0。第二个周期从第13行开始,到第27行结束,因为第27行的振幅为0,小于或等于0.5,并且出现在第二个峰值和谷值之后。 - Maral Dorri
1个回答

3

根据周期的定义,我认为你的周期应该在第12、27、36、48和62行结束,因此总共应该有6个周期,而不是5个。实际上,只有5个完整的周期。

为了简单起见和区分,一些数据/对象的名称已更改-

给定的对象

df_a <- data.frame(a = c(0,1,2,3,2,1,0,-1,-2,-3,-2,-1,
                      0,1,2,3,4,3,2,1,0,-1,-2,-3,-2,-1,
                      0,1,2,1,0,-1,-2,-3,-2,-1,0,1,2,3,2,1,
                      0,-1,-2,-3,-2,-1,0,1,2,3,2,1,
                      0,-1,-2,-3,-4,-3,-2,-1,0))
df_a$b <- seq(1,length(df_a$a),1)
df_a
my_max <- findPeaks(df_a$a)
my_min <- findValleys(df_a$a)


span <- df_a[my_max-1,]$a-df_a[my_min-1,]$a
set <-  df_a[my_max,]$a - span/2

代码循环提议

# generate a for loop to calculate end of cycle

my_vec <- NULL # create a null vector

#create a my_vec through for loop
for(i in seq_along(my_max)){
  my_vec[i] <- which(df_a$b > max(my_max[i], my_min[i]) & df_a$a >= set[i])[1]
  }

library(tidyverse) # for cumsum function
#create cycle column
df_a$cycle <- rev(cumsum(rev(df_a$b %in% my_vec)))
#check
> df_a
    a  b cycle
1   0  1     5
2   1  2     5
3   2  3     5
4   3  4     5
5   2  5     5
6   1  6     5
7   0  7     5
8  -1  8     5
9  -2  9     5
10 -3 10     5
11 -2 11     5
12 -1 12     5
13  0 13     4
14  1 14     4
15  2 15     4
16  3 16     4
17  4 17     4
18  3 18     4
19  2 19     4
20  1 20     4
21  0 21     4
22 -1 22     4
23 -2 23     4
24 -3 24     4
25 -2 25     4
26 -1 26     4
27  0 27     4
28  1 28     3
29  2 29     3
30  1 30     3
31  0 31     3
32 -1 32     3
33 -2 33     3
34 -3 34     3
35 -2 35     3
36 -1 36     3
37  0 37     2
38  1 38     2
39  2 39     2
40  3 40     2
41  2 41     2
42  1 42     2
43  0 43     2
44 -1 44     2
45 -2 45     2
46 -3 46     2
47 -2 47     2
48 -1 48     2
49  0 49     1
50  1 50     1
51  2 51     1
52  3 52     1
53  2 53     1
54  1 54     1
55  0 55     1
56 -1 56     1
57 -2 57     1
58 -3 58     1
59 -4 59     1
60 -3 60     1
61 -2 61     1
62 -1 62     1
63  0 63     0

以上代码会按照相反的顺序生成循环数字。如果需要按照顺序生成,请执行以下操作:

df_a$cycle <- max(rev(cumsum(rev(df_a$b %in% my_vec))))+1-rev(cumsum(rev(df_a$b %in% my_vec)))

df_a

> df_a
    a  b cycle
1   0  1     1
2   1  2     1
3   2  3     1
4   3  4     1
5   2  5     1
6   1  6     1
7   0  7     1
8  -1  8     1
9  -2  9     1
10 -3 10     1
11 -2 11     1
12 -1 12     1
13  0 13     2
14  1 14     2
15  2 15     2
16  3 16     2
17  4 17     2
18  3 18     2
19  2 19     2
20  1 20     2
21  0 21     2
22 -1 22     2
23 -2 23     2
24 -3 24     2
25 -2 25     2
26 -1 26     2
27  0 27     2
28  1 28     3
29  2 29     3
30  1 30     3
31  0 31     3
32 -1 32     3
33 -2 33     3
34 -3 34     3
35 -2 35     3
36 -1 36     3
37  0 37     4
38  1 38     4
39  2 39     4
40  3 40     4
41  2 41     4
42  1 42     4
43  0 43     4
44 -1 44     4
45 -2 45     4
46 -3 46     4
47 -2 47     4
48 -1 48     4
49  0 49     5
50  1 50     5
51  2 51     5
52  3 52     5
53  2 53     5
54  1 54     5
55  0 55     5
56 -1 56     5
57 -2 57     5
58 -3 58     5
59 -4 59     5
60 -3 60     5
61 -2 61     5
62 -1 62     5
63  0 63     6

旧代码

my_vec <- NULL

for(i in seq_along(my_max)){
  my_vec[1] <- 0
  my_vec[i+1] <- which(df_a$b > max(my_max[i], my_min[i]) & df_a$a >= set[i])[1]
  }
# generate column cycle as intended

df_a$cycle <- c(rep(1:length(my_max), diff(my_vec)), rep(length(my_max)+1, length(df_a$a)-length(rep(1:length(my_max), diff(my_vec)))))

    a  b cycle
1   0  1     1
2   1  2     1
3   2  3     1
4   3  4     1
5   2  5     1
6   1  6     1
7   0  7     1
8  -1  8     1
9  -2  9     1
10 -3 10     1
11 -2 11     1
12 -1 12     1
13  0 13     2
14  1 14     2
15  2 15     2
16  3 16     2
17  4 17     2
18  3 18     2
19  2 19     2
20  1 20     2
21  0 21     2
22 -1 22     2
23 -2 23     2
24 -3 24     2
25 -2 25     2
26 -1 26     2
27  0 27     2
28  1 28     3
29  2 29     3
30  1 30     3
31  0 31     3
32 -1 32     3
33 -2 33     3
34 -3 34     3
35 -2 35     3
36 -1 36     3
37  0 37     4
38  1 38     4
39  2 39     4
40  3 40     4
41  2 41     4
42  1 42     4
43  0 43     4
44 -1 44     4
45 -2 45     4
46 -3 46     4
47 -2 47     4
48 -1 48     4
49  0 49     5
50  1 50     5
51  2 51     5
52  3 52     5
53  2 53     5
54  1 54     5
55  0 55     5
56 -1 56     5
57 -2 57     5
58 -3 58     5
59 -4 59     5
60 -3 60     5
61 -2 61     5
62 -1 62     5
63  0 63     6

逻辑说明

  • 为了创建每个周期的端点,我从一个空向量开始。

  • 将该向量的第一个元素取为0

  • 使用您的循环定义创建另外一个元素(数量相同),从而使 my_vec 中的元素比需要的多一个

  • my_vec 将具有每个周期的终点

  • diff(my_vec)会产生相同数量的元素,但每个元素代表每个周期的终点

  • rep(1:完成周期数,diff(my_vec)将生成所需的向量

  • 然后,此向量将产生仅完整周期。 然后需要添加不完整周期编号。

随后,您可以连接已生成的列。

完整输出

df_b <- data.frame(cycle = 1:length(my_max))
df_b$span <- df_a[my_max-1,]$a-df_a[my_min-1,]$a
df_b$set <-  df_a[my_max,]$a - span/2

merge(df_a, df_b, by.x = "cycle", by.y = "cycle", all = T)
   cycle  a  b span  set
1      1  0  1    6 -1.0
2      1  1  2    6 -1.0
3      1  3  4    6 -1.0
4      1  2  5    6 -1.0
5      1  1  6    6 -1.0
6      1  2  3    6 -1.0
7      1 -1  8    6 -1.0
8      1 -2  9    6 -1.0
9      1 -3 10    6 -1.0
10     1  0  7    6 -1.0
11     1 -1 12    6 -1.0
12     1 -2 11    6 -1.0
13     2  0 13    7 -0.5
14     2  1 14    7 -0.5
15     2  2 15    7 -0.5
16     2  4 17    7 -0.5
17     2  3 18    7 -0.5
18     2  2 19    7 -0.5
19     2  3 16    7 -0.5
20     2  0 21    7 -0.5
21     2 -1 22    7 -0.5
22     2 -2 23    7 -0.5
23     2  1 20    7 -0.5
24     2 -2 25    7 -0.5
25     2 -1 26    7 -0.5
26     2  0 27    7 -0.5
27     2 -3 24    7 -0.5
28     3  1 28    5 -1.5
29     3  1 30    5 -1.5
30     3  0 31    5 -1.5
31     3 -1 32    5 -1.5
32     3  2 29    5 -1.5
33     3 -3 34    5 -1.5
34     3 -2 35    5 -1.5
35     3 -1 36    5 -1.5
36     3 -2 33    5 -1.5
37     4  1 38    6 -1.0
38     4  2 39    6 -1.0
39     4  3 40    6 -1.0
40     4  2 41    6 -1.0
41     4  1 42    6 -1.0
42     4  0 43    6 -1.0
43     4 -1 44    6 -1.0
44     4 -2 45    6 -1.0
45     4 -3 46    6 -1.0
46     4 -2 47    6 -1.0
47     4 -1 48    6 -1.0
48     4  0 37    6 -1.0
49     5  0 49    7 -1.5
50     5  2 51    7 -1.5
51     5  3 52    7 -1.5
52     5  2 53    7 -1.5
53     5  1 54    7 -1.5
54     5  0 55    7 -1.5
55     5 -1 56    7 -1.5
56     5 -2 57    7 -1.5
57     5 -3 58    7 -1.5
58     5 -4 59    7 -1.5
59     5 -3 60    7 -1.5
60     5 -2 61    7 -1.5
61     5 -1 62    7 -1.5
62     5  1 50    7 -1.5
63     6  0 63   NA   NA

我们可以检查它

library(tidyverse)

df_a %>% ggplot() +
  geom_line(aes(x=b, y=a, linetype = as.character(cycle)))

enter image description here


1
谢谢!我不知道为什么,但是当我使用自己的数据运行循环线时出现错误:Error in rep(1:length(max.force), diff(min.force)) : invalid 'times' argument 这里的 max.force 是你的 my_max,而 min.force 是你的 my_min。我不知道为什么,times 值(即 diff(min.force))都是正数。 - Maral Dorri
rep 中的第二个参数不是 min.force,而是 my_vec。请检查。 - AnilGoyal
@MaralDorri,我已经编辑了计算周期的公式。看看是否有帮助。 - AnilGoyal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接