R 将组织标题映射到数据框

R map org headlines to dataframe

我有一个简单的 org 文件,如下所示:

* Title
** Book 1
*** Chapter 1
*** Chapter 2
...
** Book 3
...

我想将上面的标题结构映射到数据框,以便每一章都可以与其对应的书和标题相关联,如下所示:

data.frame(title="Title", bk=c("Book 1", "Book 1", "Book 3"), ch=c("Chapter 1", "Chapter 2", "Chapter 1"))

  title     bk        ch
1 Title Book 1 Chapter 1
2 Title Book 1 Chapter 2
...
32 Title Book 3 Chapter 1

每个标题的深度可以用这个代码索引:

headlines=grep('\*', input, value=T)
id=str_count(headlines, "\*")

所以下面会给我单独的书籍和章节向量(在这种情况下,标题非常简单):

ch=headlines[id==3]
bk=headlines[id==2]

正如我们所见,ch 向量肯定会比 bk 长。以id为参考,我们如何让bk对应地重复,从而可以将它们绑定在一起形成上面的dataframe?

也欢迎其他方法。


实际数据

以下是实际数据,对应论语中的书籍和章节。对于那些看不懂中文的人,一个*是整本书的标题(collection),**是单本书,***是章节。

headlines=
c("* 論語", "** 學而第一", "*** 第1章", "*** 第2章", 
"*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", 
"*** 第8章", "*** 第9章", "*** 第10章", "*** 第11章", 
"*** 第12章", "*** 第13章", "*** 第14章", "*** 第15章", 
"*** 第16章", "** 為政第二", "*** 第1章", "*** 第2章", 
"*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", 
"*** 第8章", "*** 第9章", "*** 第10章", "*** 第11章", 
"*** 第12章", "*** 第13章", "*** 第14章", "*** 第15章", 
"*** 第16章", "*** 第17章", "*** 第18章", "*** 第19章", 
"*** 第20章", "*** 第21章", "*** 第22章", "*** 第23章", 
"*** 第24章", "** 八佾第三", "*** 第1章", "*** 第2章", 
"*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", 
"*** 第8章", "*** 第9章", "*** 第10章", "*** 第11章", 
"*** 第12章", "*** 第13章", "*** 第14章", "*** 第15章", 
"*** 第16章", "*** 第17章", "*** 第18章", "*** 第19章", 
"*** 第20章", "*** 第21章", "*** 第22章", "*** 第23章", 
"*** 第24章", "*** 第25章", "*** 第26章", "** 里仁第四", 
"*** 第1章", "*** 第2章", "*** 第3章", "*** 第4章", "*** 第5章", 
"*** 第6章", "*** 第7章", "*** 第8章", "*** 第9章", "*** 第10章", 
"*** 第11章", "*** 第12章", "*** 第13章", "*** 第14章", 
"*** 第15章", "*** 第16章", "*** 第17章", "*** 第18章", 
"*** 第19章", "*** 第20章", "*** 第21章", "*** 第22章", 
"*** 第23章", "*** 第24章", "*** 第25章", "*** 第26章", 
"** 公冶長第五", "*** 第1章", "*** 第2章", "*** 第3章", 
"*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", "*** 第8章", 
"*** 第9章", "*** 第10章", "*** 第11章", "*** 第12章", 
"*** 第13章", "*** 第14章", "*** 第15章", "*** 第16章", 
"*** 第17章", "*** 第18章", "*** 第19章", "*** 第20章", 
"*** 第21章", "*** 第22章", "*** 第23章", "*** 第24章", 
"*** 第25章", "*** 第26章", "*** 第27章", "*** 第28章", 
"** 雍也第六", "*** 第1章", "*** 第2章", "*** 第3章", 
"*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", "*** 第8章", 
"*** 第9章", "*** 第10章", "*** 第11章", "*** 第12章", 
"*** 第13章", "*** 第14章", "*** 第15章", "*** 第16章", 
"*** 第17章", "*** 第18章", "*** 第19章", "*** 第20章", 
"*** 第21章", "*** 第22章", "*** 第23章", "*** 第24章", 
"*** 第25章", "*** 第26章", "*** 第27章", "*** 第28章", 
"** 述而第七", "*** 第1章", "*** 第2章", "*** 第3章", 
"*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", "*** 第8章", 
"*** 第9章", "*** 第10章", "*** 第11章", "*** 第12章", 
"*** 第13章", "*** 第14章", "*** 第15章", "*** 第16章", 
"*** 第17章", "*** 第18章", "*** 第19章", "*** 第20章", 
"*** 第21章", "*** 第22章", "*** 第23章", "*** 第24章", 
"*** 第26章", "*** 第27章", "*** 第28章", "*** 第29章", 
"*** 第30章", "*** 第31章", "*** 第32章", "*** 第33章", 
"*** 第34章", "*** 第35章", "*** 第36章", "*** 第37章", 
"*** 第38章", "** 泰伯第八", "*** 第1章", "*** 第2章", 
"*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", 
"*** 第8章", "*** 第9章", "*** 第10章", "*** 第11章", 
"*** 第12章", "*** 第13章", "*** 第14章", "*** 第15章", 
"*** 第16章", "*** 第17章", "*** 第18章", "*** 第19章", 
"*** 第20章", "*** 第21章", "** 子罕第九", "*** 第1章", 
"*** 第2章", "*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", 
"*** 第7章", "*** 第8章", "*** 第9章", "*** 第10章", 
"*** 第11章", "*** 第12章", "*** 第13章", "*** 第14章", 
"*** 第15章", "*** 第16章", "*** 第17章", "*** 第18章", 
"*** 第19章", "*** 第20章", "*** 第21章", "*** 第22章", 
"*** 第23章", "*** 第24章", "*** 第25章", "*** 第26章", 
"*** 第27章", "*** 第28章", "*** 第29章", "*** 第30章", 
"** 鄉黨第十", "*** 第1章", "*** 第3章", "*** 第4章", 
"*** 第5章", "*** 第6章", "*** 第7章", "*** 第8章", "*** 第9章", 
"*** 第10章", "*** 第11章", "*** 第12章", "*** 第13章", 
"*** 第14章", "*** 第15章", "*** 第16章", "*** 第17章", 
"*** 第18章", "** 先進第十一", "*** 第1章", "*** 第2章", 
"*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", 
"*** 第8章", "*** 第9章", "*** 第10章", "*** 第11章", 
"*** 第12章", "*** 第13章", "*** 第14章", "*** 第15章", 
"*** 第16章", "*** 第17章", "*** 第19章", "*** 第21章", 
"*** 第21章", "*** 第22章", "*** 第23章", "*** 第24章", 
"** 顏淵第十二", "*** 第1章", "*** 第3章", "*** 第4章", 
"*** 第5章", "*** 第5章", "*** 第6章", "*** 第7章", "*** 第8章", 
"*** 第9章", "*** 第10章", "*** 第11章", "*** 第12章", 
"*** 第13章", "*** 第14章", "*** 第15章", "*** 第16章", 
"*** 第17章", "*** 第18章", "*** 第19章", "*** 第20章", 
"*** 第21章", "*** 第22章", "*** 第23章", "*** 第24章", 
"** 子路第十三", "*** 第1章", "*** 第2章", "*** 第3章", 
"*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", "*** 第8章", 
"*** 第9章", "*** 第10章", "*** 第11章", "*** 第12章", 
"*** 第13章", "*** 第14章", "*** 第15章", "*** 第16章", 
"*** 第17章", "*** 第18章", "*** 第19章", "*** 第20章", 
"*** 第21章", "*** 第22章", "*** 第23章", "*** 第24章", 
"*** 第25章", "*** 第26章", "*** 第27章", "*** 第28章", 
"*** 第29章", "*** 第30章", "** 憲問第十四", "*** 第1章", 
"*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", 
"*** 第8章", "*** 第9章", "*** 第10章", "*** 第11章", 
"*** 第12章", "*** 第13章", "*** 第14章", "*** 第15章", 
"*** 第16章", "*** 第17章", "*** 第18章", "*** 第19章", 
"*** 第20章", "*** 第21章", "*** 第22章", "*** 第23章", 
"*** 第24章", "*** 第25章", "*** 第26章", "*** 第27章", 
"*** 第28章", "*** 第29章", "*** 第30章", "*** 第31章", 
"*** 第32章", "*** 第33章", "*** 第34章", "*** 第35章", 
"*** 第36章", "*** 第37章", "*** 第38章", "*** 第39章", 
"*** 第41章", "*** 第42章", "*** 第43章", "*** 第44章", 
"*** 第45章", "*** 第46章", "*** 第47章", "** 衛靈公第十五", 
"*** 第1章", "*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", 
"*** 第7章", "*** 第8章", "*** 第9章", "*** 第10章", 
"*** 第11章", "*** 第12章", "*** 第13章", "*** 第14章", 
"*** 第15章", "*** 第16章", "*** 第17章", "*** 第18章", 
"*** 第19章", "*** 第20章", "*** 第21章", "*** 第22章", 
"*** 第23章", "*** 第24章", "*** 第25章", "*** 第26章", 
"*** 第27章", "*** 第28章", "*** 第29章", "*** 第30章", 
"*** 第31章", "*** 第32章", "*** 第33章", "*** 第34章", 
"*** 第35章", "*** 第36章", "*** 第37章", "*** 第38章", 
"*** 第39章", "*** 第40章", "*** 第41章", "*** 第42章", 
"** 季氏第十六", "*** 第1章", "*** 第2章", "*** 第3章", 
"*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", "*** 第8章", 
"*** 第9章", "*** 第10章", "*** 第11章", "*** 第12章", 
"*** 第13章", "*** 第14章", "** 陽貨第十七", "*** 第1章", 
"*** 第2章", "*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", 
"*** 第7章", "*** 第8章", "*** 第9章", "*** 第10章", 
"*** 第11章", "*** 第12章", "*** 第13章", "*** 第14章", 
"*** 第15章", "*** 第16章", "*** 第17章", "*** 第18章", 
"*** 第19章", "*** 第20章", "*** 第21章", "*** 第22章", 
"*** 第23章", "*** 第24章", "** 微子第十八", "*** 第1章", 
"*** 第2章", "*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", 
"*** 第7章", "*** 第8章", "*** 第9章", "*** 第10章", 
"*** 第11章", "** 子張第十九", "*** 第1章", "*** 第2章", 
"*** 第3章", "*** 第4章", "*** 第5章", "*** 第6章", "*** 第7章", 
"*** 第8章", "*** 第9章", "*** 第10章", "*** 第11章", 
"*** 第12章", "*** 第13章", "*** 第14章", "*** 第15章", 
"*** 第16章", "*** 第17章", "*** 第18章", "*** 第19章", 
"*** 第20章", "*** 第21章", "*** 第22章", "*** 第23章", 
"*** 第24章", "*** 第25章", "** 堯曰第二十", "*** 第1章", 
"*** 第2章", "*** 第3章")

所以我正在寻找这样的东西:

   title          bk        ch
1 * 論語 ** 學而第一 *** 第1章
2 * 論語 ** 學而第一 *** 第2章
...
41 * 論語 ** 八佾第三 *** 第1章

有几种方法是可行的。一种方法:

library(tidyr)
library(dplyr)
headlines <- c("Book 1", "Chapter 1", "Chapter 2", "Book 2", "Chapter 1", "Chapter 2")
id        <- c(1, 2, 2, 1, 2, 2)

df <- data.frame(ID=id, Book=headlines, Chapter=headlines)
df %>% mutate(Book=ifelse(id == 1, Book, NA)) %>% fill(Book) %>% filter(id == 2)

不使用 tidyverse:

books <- headlines[ id == 1 ]
chapters <- headlines[ id == 2 ]
rl <- rle(id)
reps <- rl$lengths[ rl$values == 2 ]
books <- unlist(lapply(1:length(books), function(i) rep(books[i], reps[i])))
df <- data.frame(Books=books, Chapters=chapters)

这是另一种基本 R 方法,假设您的章节对于每个 book

data.frame(title="Title", 
    bk = bk[cumsum(c(1, diff(as.integer(sub(".*?(\d+).*", "\1", ch))) < 0))], 
    ch = ch)

#  title        bk            ch
#1 Title ** Book 1 *** Chapter 1
#2 Title ** Book 1 *** Chapter 2
#3 Title ** Book 1 *** Chapter 3
#4 Title ** Book 3 *** Chapter 1
#5 Title ** Book 3 *** Chapter 2

因为 titlech 已经被提取并且达到了要求的长度,我们不会改变它。对于bk让我们理解一下step-by-step

我们先把ch的数字部分提取出来,转换成整数

as.integer(sub(".*?(\d+).*", "\1", ch))
#[1] 1 2 3 1 2

然后创建一个索引,每当数字部分从其先前的值减少时该索引就会递增

cumsum(c(1, diff(as.integer(sub(".*(?\d+).*", "\1", ch))) < 0))
#[1] 1 1 1 2 2

使用此索引重复 bk

的值
bk[cumsum(c(1, diff(as.integer(sub(".*?(\d+).*", "\1", ch))) < 0))]
#[1] "** Book 1" "** Book 1" "** Book 1" "** Book 3" "** Book 3"

数据

ch <- c("*** Chapter 1", "*** Chapter 2", "*** Chapter 3", "*** Chapter 1", 
"*** Chapter 2")
bk  <- c("** Book 1", "** Book 3")

编辑

看起来它也适用于更新

data.frame(title="Title", 
        bk = bk[cumsum(c(1, diff(as.integer(sub(".*?(\d+).*", "\1", ch))) < 0))], 
        ch = ch)


#    title         bk         ch
#1   Title   ** 學而第一  *** 第1章
#2   Title   ** 學而第一  *** 第2章
#3   Title   ** 學而第一  *** 第3章
#4   Title   ** 學而第一  *** 第4章
#5   Title   ** 學而第一  *** 第5章
#6   Title   ** 學而第一  *** 第6章
#7   Title   ** 學而第一  *** 第7章
#8   Title   ** 學而第一  *** 第8章
#9   Title   ** 學而第一  *** 第9章
#10  Title   ** 學而第一 *** 第10章
#....

@January 上面的第二个解决方案适用于实际数据,只需更改一些 id 值即可。

books <- headlines[ id == 2 ]
chapters <- headlines[ id == 3 ]
rl <- rle(id)
reps <- rl$lengths[ rl$values == 3 ]
books <- unlist(lapply(1:length(books), function(i) rep(books[i], reps[i])))
df <- data.frame(Books=books, Chapters=chapters)