📝

TIF:テキスト分析用Rパッケージのデザインの紹介

2022/02/24に公開約4,700字

R Advent Calendar 2021 1日目でした

https://qiita.com/advent-calendar/2021/rlang

Text Interchange Formats(TIF)とは?

Text Interchange Formats(TIF)は、2017年にrOpenSci Text Workshopで整備された「こんな感じでテキスト分析用のRパッケージをデザインしましょう!」という仕様みたいなものです。

まだドラフト段階らしいのですが、たとえばquantedaなどはこの仕様に沿ってデザインされているようです。

実際のところ、この仕様に沿ったからといってそれ自体ではどうということでもないのですが、一応、他のパッケージと組み合わせて使いやすくなるだろうというモチベーションはあって、筆者が個人で開発しているパッケージの一部でもこの仕様を意識しています。

以下では、TIFってこんな感じの見た目になるやつですということを自作パッケージにおける実装の例を交えながら、ごく簡単に紹介します。

Corpus:コーパス

TIFではコーパス(corpus)、文書単語行列(dtm)、トークン(token)という要素が定義されています。

このうち文書の集合であるコーパスは、データフレームか名前付きベクトルの形式で保持します。いずれも文字エンコーディングはUTF-8であることが前提されています。

Corpus(data frame)

データフレーム形式のコーパスは、少なくともdoc_idtextという列を含むデータフレームです。doc_id列は文書によって一意なID列(character型である必要がある)で、text列は文書本体です。「少なくとも」なので、このほかの列に文書のメタ情報などが含まれていてもよいことになっています。

ちなみに、このかたちのデータフレームはreadtextパッケージを使うと簡単に得ることができます。

corp1 <- data.frame(
  doc_id = c("text1", "text2", "text3"),
  text = c("ひとつめの文書", "ふたつめの文書", "みっつめの文書")
)

Corpus(character vector)

ベクトル形式のコーパスは、文書のIDを各要素の名前にした、名前付きの文字列ベクトル(named character vector)です。

corp2 <- c(
  "text1" = "ひとつめの文書",
  "text2" = "ふたつめの文書",
  "text3" = "みっつめの文書"
)

なお、筆者が開発している形態素解析ができるパッケージでは、基本的にこの形式のコーパスから処理することを想定しています。文書レベルのメタデータも保持したくて、データフレーム形式のコーパスと結合したい場合などには、次のようなかたちで分かち書きした文書をtext列に持つコーパスを経由することができます。

library(magrittr)
corp2 <- c(
    "text1" = "ひとつめの文書",
    "text2" = "ふたつめの文書",
    "text3" = "みっつめの文書"
)
rjavacmecab::igo(corp2) %>% 
    rjavacmecab::prettify() %>% 
    rjavacmecab::pack()
#>   doc_id              text
#> 1  text1 ひと つめ の 文書
#> 2  text2 ふたつ め の 文書
#> 3  text3 みっ つめ の 文書

dtm:文書単語行列

いわゆる「文書単語行列(document-term matrix, DTM)」は、各行が文書で、各列が単語である疎行列オブジェクト(dgCMatrix of 'Matrix' package)です(quantedaの実装があるのでここは自分で再実装することはなさそう)。

tokens:トークン

トークンは、コーパスを文書ごとにいい感じの単位にまとめあげながら格納したものです。それぞれのトークンは単語とかngramとかだったりを想定しています。

tokens(data frame)

データフレーム形式のトークンは、各行が一つのトークンであるようなデータフレームです。少なくともdoc_id列(character型である必要がある)とtoken列が含まれる必要があります。以下のような感じのやつで、実際にはトークンごとのメタデータ(品詞情報など)が付くことになります。

toks1 <- data.frame(
  doc_id = c("text1", "text1", "text1", "text1",
             "text2", "text2", "text2", "text2",
             "text3", "text3", "text3", "text3"),
  token =  c("ひとつ", "め", "の", "文書",
             "ふたつ", "め", "の", "文書",
             "みっつ", "め", "の", "文書")
)

したがって、だいたい次のようなかたちのデータフレームになります。

library(magrittr)
corp2 <- c(
    "text1" = "ひとつめの文書",
    "text2" = "ふたつめの文書",
    "text3" = "みっつめの文書"
)
rjavacmecab::igo(corp2) %>%
    rjavacmecab::prettify()
#>    doc_id  token   POS1     POS2 POS3 POS4 X5StageUse1 X5StageUse2 Original
#> 1   text1   ひと 接頭詞 名詞接続 <NA> <NA>        <NA>        <NA>     ひと
#> 2   text1   つめ   名詞     一般 <NA> <NA>        <NA>        <NA>     つめ
#> 3   text1     の   助詞   連体化 <NA> <NA>        <NA>        <NA>       の
#> 4   text1   文書   名詞     一般 <NA> <NA>        <NA>        <NA>     文書
#> 5   text2 ふたつ   名詞     一般 <NA> <NA>        <NA>        <NA>   ふたつ
#> 6   text2     め   名詞     接尾 一般 <NA>        <NA>        <NA>       め
#> 7   text2     の   助詞   連体化 <NA> <NA>        <NA>        <NA>       の
#> 8   text2   文書   名詞     一般 <NA> <NA>        <NA>        <NA>     文書
#> 9   text3   みっ   動詞     自立 <NA> <NA>  五段・タ行  連用タ接続     みつ
#> 10  text3   つめ   名詞     一般 <NA> <NA>        <NA>        <NA>     つめ
#> 11  text3     の   助詞   連体化 <NA> <NA>        <NA>        <NA>       の
#> 12  text3   文書   名詞     一般 <NA> <NA>        <NA>        <NA>     文書
#>       Yomi1    Yomi2
#> 1      ヒト     ヒト
#> 2      ツメ     ツメ
#> 3        ノ       ノ
#> 4  ブンショ ブンショ
#> 5    フタツ   フタツ
#> 6        メ       メ
#> 7        ノ       ノ
#> 8  ブンショ ブンショ
#> 9      ミッ     ミッ
#> 10     ツメ     ツメ
#> 11       ノ       ノ
#> 12 ブンショ ブンショ

tokens(list)

リスト形式のトークンは、文書のIDを各要素の名前にした、文字列ベクトルのリスト(possibly named list of character vectors)です。各要素がそのままトークンになるため、こちらにはトークンレベルのメタデータは一切付きません。

toks2 <- list(
  "text1" = c("ひとつ", "め", "の", "文書"),
  "text2" = c("ふたつ", "め", "の", "文書"),
  "text3" = c("みっつ", "め", "の", "文書")
)

実際には、次のようなかたちになります。

corp2 <- c(
    "text1" = "ひとつめの文書",
    "text2" = "ふたつめの文書",
    "text3" = "みっつめの文書"
)
rjavacmecab::igo(corp2, mode = "wakati")
#> $text1
#> [1] "ひと" "つめ" "の"   "文書"
#> 
#> $text2
#> [1] "ふたつ" "め"     "の"     "文書"  
#> 
#> $text3
#> [1] "みっ" "つめ" "の"   "文書"

参考

GitHubで編集を提案

Discussion

ログインするとコメントできます