假设字符串序列表示大纲中的部分编号,这是一个可能的解决方案:
library(data.table)
# reshape to long format
long <- DT[, rn := .I][
, strsplit(No, "[.]"), by = rn][
, V1 := as.integer(V1)][
, lvl := rowid(rn)][]
# find contiguous streaks of rows where there is a gap in levels
nlvl <- long[, .N, keyby = rn][
, gap := cumsum(c(diff(N), 0L) > 1L)][
, M := first(N) + 1L, by = gap]
# non-equi anti-join and recast
long[!nlvl[N > M], on = .(rn, lvl >= M, lvl < N)][
, .(No = paste(V1, collapse = ".")), by = rn]
rn No
1: 1 48.8.1.1
2: 2 48.8.1.2
3: 3 48.8.2
4: 4 48.9
5: 5 48.10
6: 6 48.11
7: 7 48.11.1
8: 8 48.11.1.1
9: 9 48.11.1.2
10: 10 48.11.1.2.1
11: 11 48.11.1.2.2
12: 12 48.11.1.2.3
13: 13 48.11.2
14: 14 48.11.2.1
15: 15 48.11.2.1.1
16: 16 48.11.3
17: 17 48.11.3.1
18: 18 50
说明
如果我理解正确的话,字符串序列表示大纲中的部分编号,这些部分已通过插入虚假的附加级别而被扭曲。 OP 正在寻找一种方法来删除这些额外的级别。
所以,让我们假设一些规则来对大纲的各个部分进行编号:
- 节号间隙:在同一级别上,节号始终提前 1。
例如,遵循4.1.3 部分
-
4.1.4 (第 3 部分提前)
- 或
4.2(如果父级高级)
- 或
5(前进到下一章)。
- 级别差距:子级别始终比父级别低 1 级。子级的节编号从 1 开始。
例如,4.1.3 部分后面可以跟 4.1.3.1 作为子部分,但 不是
4.1.3.1.1 从第 3 级跳转到第 5 级子子节。
由于 OP 问题可能与情况 2 相关,我们需要用
相对于前几行的水平差距。作为修复,所有中间盈余水平将被删除。最后一层被保留,因为它的编号可能是正确的。
第一步包括添加行号,在"." 处拆分字符串,将节号强制为整数,并对每行的级别进行编号。现在,节号是长格式:
long
rn V1 lvl
1: 1 48 1
2: 1 8 2
3: 1 1 3
4: 1 1 4
5: 2 48 1
6: 2 8 2
[...]
25: 9 48 1
26: 9 11 2
27: 9 1 3
28: 9 2 4
29: 10 48 1
30: 10 11 2
31: 10 1 3
32: 10 2 4
33: 10 2 5
34: 10 2 6
35: 10 2 7
36: 10 1 8
37: 11 48 1
38: 11 11 2
39: 11 1 3
40: 11 2 4
41: 11 2 5
42: 11 2 6
43: 11 2 7
44: 11 2 8
45: 12 48 1
46: 12 11 2
47: 12 1 3
48: 12 2 4
49: 12 2 5
50: 12 3 6
51: 13 48 1
52: 13 11 2
53: 13 2 3
54: 14 48 1
55: 14 11 2
56: 14 2 3
57: 14 1 4
58: 15 48 1
59: 15 11 2
60: 15 2 3
61: 15 1 4
62: 15 1 5
63: 16 48 1
64: 16 11 2
65: 16 3 3
66: 17 48 1
67: 17 11 2
68: 17 3 3
69: 17 3 4
70: 17 3 5
71: 17 1 6
72: 18 50 1
rn V1 lvl
在下一步中,将识别出现在节级别的间隙之后的连续行条纹。节级别是属于一行的元素的数量N。 gap 标记在节级别非法跳转之后的所有行。 M 表示根据上述规则 2 合法的最大部分级别。
nlvl
rn N gap M
1: 1 4 0 5
2: 2 4 0 5
3: 3 3 0 5
4: 4 2 0 5
5: 5 2 0 5
6: 6 2 0 5
7: 7 3 0 5
8: 8 4 0 5
9: 9 4 1 5
10: 10 8 1 5
11: 11 8 1 5
12: 12 6 1 5
13: 13 3 1 5
14: 14 4 1 5
15: 15 5 1 5
16: 16 3 2 4
17: 17 6 2 4
18: 18 1 2 4
在最后一步中,通过非等值反连接从long 中消除额外的虚假级别。然后,通过将各个部分粘贴在一起来重新创建部分编号。
为了比较,我们可以将结果与原始数据连接起来:
long[!nlvl[N > M], on = .(rn, lvl >= M, lvl < N)][
, .(No = paste(V1, collapse = ".")), by = rn][DT, on = "rn"]
rn No i.No
1: 1 48.8.1.1 48.8.1.1.
2: 2 48.8.1.2 48.8.1.2.
3: 3 48.8.2 48.8.2.
4: 4 48.9 48.9.
5: 5 48.10 48.10.
6: 6 48.11 48.11.
7: 7 48.11.1 48.11.1.
8: 8 48.11.1.1 48.11.1.1.
9: 9 48.11.1.2 48.11.1.2.
10: 10 48.11.1.2.1 48.11.1.2.2.2.2.1.
11: 11 48.11.1.2.2 48.11.1.2.2.2.2.2.
12: 12 48.11.1.2.3 48.11.1.2.2.3.
13: 13 48.11.2 48.11.2.
14: 14 48.11.2.1 48.11.2.1.
15: 15 48.11.2.1.1 48.11.2.1.1.
16: 16 48.11.3 48.11.3.
17: 17 48.11.3.1 48.11.3.3.3.1.
18: 18 50 50.
数据
OP 发布的数据的扩展版本。
library(data.table)
DT <- data.table(No = c(
"48.8.1.1.",
"48.8.1.2.",
"48.8.2." ,
"48.9." ,
"48.10." ,
"48.11." ,
"48.11.1." ,
"48.11.1.1." ,
"48.11.1.2." ,
"48.11.1.2.2.2.2.1.",
"48.11.1.2.2.2.2.2.",
"48.11.1.2.2.3.",
"48.11.2.",
"48.11.2.1.",
"48.11.2.1.1.",
"48.11.3.",
"48.11.3.3.3.1.",
"50."
))