0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

計量経済学 × テキスト解析でプロダクトデザインを提案する方法

0
Posted at

はじめに

こんにちは、事業会社で働いているデータサイエンティストです。

近年、LLMの発展によって、テキストデータを扱うハードルは急激に下がりました。

文章を分類したい、要約したい、必要な情報を抽出したい、といった問題であれば、とりあえずLLMに文章を渡してみる、という選択肢が普通に考えられるようになっています。

では、LLM以前から存在していたテキスト解析の手法は、もう必要ないのでしょうか?

もちろん、そんなことはありません。

例えばプロダクト開発では、

大量のテキストの中に埋まっている情報から、次にプロダクトへ追加すべき構造化項目の候補を探したい

という問題を考えることができます。

この問題に対して、LLMや人間に、

ユーザーが重視していそうな項目を考えてください

と聞くこともできます。

しかし、この問いはそのままではかなり曖昧です。

ブランド、色、地域、用途など、もっともらしい仮説はいくらでも考えられますし、その後で仮説を支持するデータを探すこともできます。

それ自体が悪いわけではありません。

一方で、もし解きたい問題を統計的にもう少し明確に定義でき、そのための専用の道具が存在するのであれば、あえてその道具を使ってみる価値もあるはずです。

そこで今回は、計量経済学のテキスト分析で使われてきた 分散多項回帰(Distributed Multinomial Regression, DMR) という手法を使って、

既存の構造化情報を考慮したうえで、それでも目的変数と関連しているテキスト上の特徴を探す

という問題を考えてみます。

さらに、その結果を単なる「重要単語ランキング」で終わらせるのではなく、プロダクトに新しく追加すべき構造化項目を考えるための探索的な道具として使ってみます。

実証例には、日本政府が公開している政府調達のオープンデータを利用します。

LLM全盛の時代に、なぜわざわざ計量経済学由来のテキストモデルを使うのか。

そして、そのような古典的な道具がプロダクト開発でどのように役立つ可能性があるのか。

今回は実際の日本語データを分析しながら考えてみます。

まずはプロダクトデザインの問題として考えてみる

新しいECサイトを作ることになった

まずは、今回考えたい問題をプロダクト開発の例から考えてみます。

例えば、新しいECサイトを立ち上げることになったとします。

商品名や商品説明のテキストは大量に集まっています。一方で、立ち上げたばかりなので、商品の情報はまだ十分に構造化されていません。

ここで、

ユーザーが商品を探しやすくするために、どんな項目を検索条件として追加すればよいのか?

という問題を考えてみます。

もちろん、すべてをデータから決める必要はありません。

例えば、なぜか法律で、

iPhoneを購買した瞬間の天気を記録しなければならない

と決められているのであれば、それがユーザー行動に影響するかどうかを分析している必要はあまりないです。

なぜそんな法律があるのか気になるのであれば国会議員にでも相談してくださいw天気は大事ですが。

少なくともプロダクトとしては、トラブルを避けるために、一旦素直に記録しておいたほうがよさそうです。

あるいは、プロダクト責任者から、

商品の「色」は絶対に検索できるようにしたい

と言われることもあるでしょう。

データを見ると色と購入率にはほとんど関係がないかもしれません。

しかし、彼女には実現したいプロダクトの思想があるのかもしれませんし、これから予定している機能との関係で色が必要なのかもしれません。

この場合も、いきなり回帰分析を持ってきて、

色の係数は有意ではありませんでした!!!!!

と勝利宣言するより、まずは話を聞いたほうがよさそうです。

一方で、法律上の要請でもなく、プロダクトとして明確な仮説があるわけでもない場合には、実際のユーザー行動データから、どんな情報を構造化する価値がありそうなのかを探索することができます。

テキストの中に、まだ構造化されていない情報がある

例えば、商品の説明文に次のような情報が含まれているとします。

  • 世界的に知られているブランドなのか
  • ハンドメイドなのか
  • 限定商品なのか
  • 特定の用途に向いているのか
  • 特定の素材を使っているのか

しかし、これらはすべて自由記述の中に埋まっていて、ECサイト上では検索できません。

ここで、例えば「世界的に知られているブランド」であることを示す言葉が、ユーザーの購入と強く関連していることが分析で分かったとします。

そうであれば、

international_brand = TRUE

のような構造化された項目として持たせることを検討できそうです。

そうすれば、ユーザーは大量の商品説明を一つずつ読まなくても、

世界的に展開しているブランドの商品だけを表示する

といった検索ができるようになります。

推薦システムや分析でも、その情報を簡単に利用できます。

これはLLMエージェントが商品を探す場合でも同じです。

もちろん、LLMに毎回商品説明を読ませて、

この商品は世界的に知られているブランドの商品だろうか?

と推論させることもできます。

しかし、それがユーザーにとって繰り返し重要になる情報だと分かっているのであれば、最初から

international_brand = TRUE

と持っておけば、エージェントもそのフラグを検索すれば済みます。

つまり、

テキストから推論できる情報

と、

プロダクトの構造として明示的に持っておく価値がある情報

は同じではありません。

LLMによって前者が非常に簡単になったとしても、後者を考える必要がなくなるわけではありません。

では、購入と関連する単語を探せばよい?

ここまでなら、

じゃあ、商品説明の中から購入と関連している単語を探せばいいのでは?

と思うかもしれません。

しかし、ここでもう一つ問題があります。

例えば、このECサイトではすでに製造元の都道府県を構造化していて、

prefecture = "東京都"

という項目を検索に利用できるとします。

さらに、何らかの理由で東京都の商品は他の地域の商品より購入されやすかったとします。

この状態で、商品説明と購入行動の関係だけを分析すると、

東京
新宿
下北沢

といった単語が「購入と強く関連する単語」として見つかるかもしれません。

考えてみれば当然です。

東京都の商品では「東京」「新宿」「下北沢」といった単語が登場しやすく、

$$
\text{東京都}
\rightarrow
{\text{東京、新宿、下北沢、...}}
$$

同時に、東京都の商品が購入されやすいのであれば、

$$
\text{東京都}
\rightarrow
\text{購入}
$$

という関係もあります。

すると、単純にテキストと購入の関係を見るだけでは、

$$
\text{新宿}
\longleftrightarrow
\text{購入}
$$

という関連が見つかります。

モデルからすると、これは立派な発見です。

しかし、プロダクト開発をしている私たちからすると、

東京都の商品には東京っぽい単語が書いてあり、東京都の商品はよく売れます!!!!!

と言われても困ります。

それ、もう prefecture という構造化項目で持ってるんだよw

私たちが探しているのは、既存のプロダクトがすでに持っている情報をテキストから再発見することではありません。

既存の構造では説明できない情報を探したい

そこで、本当に知りたい問題を少し変えてみます。

すでに持っている構造化情報を考慮したうえでも、ユーザー行動と関連しているテキスト上の特徴は何なのか?

先ほどの例であれば、都道府県の違いを考慮したあとに、

東京
新宿
下北沢

といった単語の関連が消えたとしても、

世界的ブランド
ハンドメイド
限定

といった特徴が購入と関連したまま残るかもしれません。

ここで初めて、

「世界的なブランドかどうか」という情報を、自由記述の中に埋めたままにせず、検索可能な項目として追加するとユーザーにとって便利なのでは?

というプロダクト上の仮説を作ることができます。

もちろん、これだけで

世界的なブランドだからユーザーが購入した

という因果関係が証明されるわけではありません。

候補が見つかったあとにユーザーインタビューをしてもよいですし、実際に検索項目を追加してA/Bテストをしてもよいでしょう。

ここで欲しいのは、プロダクトの仕様を自動的に決定してくれる神託ではありません。

欲しいのは、

大量の自由記述の中から、既存の構造化項目とは別に、次にプロダクトへ構造化する価値がありそうな情報の候補を探す

ための道具です。

「AIに聞けばいい」のか?

ここで、

そんなもの、LLMに「ユーザーが重視している項目を考えて」と聞けばよいのでは?

と思うかもしれません。

もちろん、それも一つの方法です。

そしてこれはLLMだけの問題でもありません。

人間の専門家に、

このECサイトでは、次にどんな検索項目を追加したらよいと思いますか?

と聞いても同じです。

ブランド、色、原産国、素材、用途など、経験からもっともらしい仮説を考えることができます。

そして、その仮説を思いついたあとで実データを調べて、

やっぱりブランドが重要でした!

という証拠を探すこともできます。

それ自体が悪いわけではありません。

プロダクト開発では、専門家の知識や仮説は当然重要です。

しかし、

ユーザーが重視している項目を探す

という問いは、そのままではかなり曖昧です。

何をもって「重視している」とするのか、どの候補を調べるのか、どこまで既存項目との重複を許すのかによって、答えはいくらでも変わります。

一方で、今回やりたいことはもう少し明確に定義できます。

$$
\boxed{
\text{既存の構造化変数を考慮したうえで、それでも目的変数と関連するテキスト特徴を探す}
}
$$

このように問題を定義できるのであれば、そのために作られた統計的な道具を使って、データ全体に同じルールを適用してみる価値があります。

そして今回、この問題を考えるために使うのが DMR です。

では次に、架空のECサイトからいったん離れて、実際の日本語データでこの問題を考えてみましょう。

政府調達のオープンデータで考えてみる

ECサイトの代わりに政府調達を使ってみる

ここからは、先ほどの架空ECサイトと似た問題を、実際に公開されている日本語データで考えてみます。

今回利用するのは、デジタル庁が運営している調達ポータルの 落札実績オープンデータ です。

調達ポータル「落札実績オープンデータのダウンロード」

調達ポータルでは、政府電子調達システムに登録された落札実績をオープンデータとして公開しており、年度ごとの全件データをCSV形式でダウンロードできます。ファイルはZIP形式で圧縮されており、本記事執筆時点では2013年度から2026年度までの全件データが掲載されています。

今回の記事では、このうち2025年度の全件データを利用します。

では、なぜECサイトのプロダクトデザインを考える記事で、突然政府調達を分析するのでしょうか。

先ほどのECサイトでは、

ユーザーがどんな商品情報を気にしているのか?

を考えたいという問題がありました。

しかし、実際の企業の

  • 商品説明
  • 商品属性
  • 購入履歴

がまとめて公開されている日本語データを都合よく入手するのは簡単ではありません。

そこで今回は、問題の構造が似ている政府調達データを使ってみます。

政府調達には、例えば、

  • どの府省が調達したのか
  • どのような入札方式だったのか
  • どのような案件なのか
  • 最終的な落札価格はいくらだったのか

といった情報があります。

そして「どのような案件なのか」は、案件名という日本語のテキストとして記録されています。

つまり、

$$
\text{既存の構造化情報}
+
\text{自由記述のテキスト}
+
\text{目的変数}
$$

という、先ほどのECサイトとよく似た構造を作ることができます。

予算が大きいほど国民が気にしている👀ことにしてみる

ECサイトでは、購入などのユーザー行動と関連するテキスト上の特徴を探したいと考えました。

政府調達では、その代わりに落札価格を目的変数として使ってみます。

かなり乱暴に、

より大きなお金が使われている案件ほど、政府にとって重要な何らかの要素を含んでいるのでは?

と考えて、そのような要素が案件名のどこに現れているのかを探してみます。

もちろん、

落札価格が高い = 国民がその政策を重視している

という意味ではありません。

落札価格は、案件の規模、必要な設備、人員、契約期間、制度、調達方式など、さまざまな要因で決まります。

したがって、本記事で

$$
\text{落札価格}
$$

を使うのは、「国民の関心」を厳密に測定するためではありません。

今回重要なのは、

すでに持っている構造化情報を考慮したあとにも、目的変数と関連する情報がテキストの中に残っているか?

という、先ほどのプロダクト開発と同じ問題を実データで作ることです。

この意味で、政府調達データをECサイトの問題を再現するための実証用の比喩として利用します。

どんなデータが公開されているのか

調達ポータルには、落札実績オープンデータのファイル仕様も公開されています。

落札実績オープンデータファイル仕様

仕様書によると、落札実績オープンデータはCSV形式およびJSON形式で提供されており、前月末時点で公開されている情報をまとめた全件データと、日次で追加された情報を収録した差分データがあります。また、ダウンロード画面から取得するだけでなく、ダウンロード用URLへ直接アクセスする方法も公式に案内されています。

今回利用するCSVには、例えば次のような情報が含まれています。

  • 調達案件番号
  • 調達案件名称
  • 落札決定日
  • 落札価格
  • 府省コード
  • 入札方式コード
  • 落札事業者名
  • 法人番号

今回特に注目するのは、

$$
Y=\text{落札価格}
$$

という目的変数と、

$$
D=\text{調達案件名称}
$$

という日本語テキスト、

そして、

$$
X=
{
\text{府省},
\text{入札方式}
}
$$

という、すでに構造化されている情報です。

これは先ほどのECサイトで考えた、

$$
Y=\text{購入などのユーザー行動}
$$

$$
D=\text{商品説明}
$$

$$
X=\text{都道府県など既存の商品属性}
$$

という構造に対応します。

本当に自由に使ってよいオープンデータなのか

公開データを技術記事で利用するときには、分析方法以前に、

そもそもこのデータを使って記事を書いて大丈夫なのか?

を確認しておく必要があります。

調達ポータルには、コンテンツの利用条件が公式に掲載されています。

調達ポータル「ご利用にあたって」

利用条件では、調達ポータルで公開されている対象コンテンツについて、複製、公衆送信、翻訳・変形などを含めて自由に利用でき、商用利用も可能であることが明記されています。編集・加工して利用することも認められており、その場合には出典に加えて加工したことを記載する必要があります。さらに、この利用ルールは政府標準利用規約2.0に準拠し、CC BY 4.0と互換性があることも明記されています。

したがって、本記事では例えば、

出典:調達ポータル(デジタル庁)「落札実績オープンデータ」を筆者が加工・分析

と明記したうえで分析を行います。

また、読者も同じ年度のデータを公式サイトから直接取得できます。

つまり今回の分析は、

私のPCにだけ存在する謎のCSV

でも、

どこかの誰かがスクレイピングしてHugging Faceにアップロードしたデータ

でもありません。

政府自身がオープンデータとして公開しているデータを、同じ公開データから誰でも再現できる形で利用します。

では、この政府調達データを使って、

既存の構造化情報を無視してテキストだけを見ると何が起こるのか?

そして、

既存の情報を考慮したあとに、どんなテキスト上の特徴が残るのか?

を見ていきましょう。

データの準備

ここから実際にデータを取得していきます。

今回利用するのは、前節で紹介した調達ポータルの2025年度の落札実績全件データです。

調達ポータルでは、年度ごとのデータをZIP形式で直接取得できるURLが用意されています。

まず、CSVに含まれる列名を定義します。

cols <- c(
  "procurement_item_no",
  "procurement_item_name",
  "award_date",
  "award_price",
  "ministry_code",
  "bidding_method_code",
  "supplier_name",
  "corporate_number"
)

今回の記事で主に利用するのは、

  • procurement_item_name:調達案件名称
  • award_price:落札価格
  • ministry_code:府省コード
  • bidding_method_code:入札方式コード

です。

一方で、supplier_namecorporate_number は今回の分析では利用しません。

続いて、2025年度全件データのファイル名とダウンロードURLを作ります。

filename <- "successful_bid_record_info_all_2025.zip"

url <- paste0(
  "https", "://", "api.p-portal.go.jp",
  "/pps-web-biz/UAB03/OAB0301",
  "?fileversion=v001&filename=",
  filename
)

あとは、RからそのままZIPファイルをダウンロードします。

zip_file <- tempfile(fileext = ".zip")
csv_dir <- tempfile()

dir.create(csv_dir)

download.file(
  url,
  zip_file,
  mode = "wb"
)

ダウンロードしたZIPファイルを展開します。

csv_files <- unzip(
  zip_file,
  exdir = csv_dir
)

ZIPの中に含まれているCSVをまとめて読み込みます。

procurement_df <-
  csv_files[
    grepl("\\.csv$", csv_files, ignore.case = TRUE)
  ] |>
  purrr::map_dfr(
    \(x)
      readr::read_csv(
        x,
        col_names = cols,
        col_types = readr::cols(
          procurement_item_no = readr::col_character(),
          procurement_item_name = readr::col_character(),
          award_date = readr::col_date(),
          award_price = readr::col_double(),
          ministry_code = readr::col_character(),
          bidding_method_code = readr::col_character(),
          supplier_name = readr::col_character(),
          corporate_number = readr::col_character()
        ),
        show_col_types = FALSE
      )
  )

これで、2025年度の政府調達データをR上でそのまま扱えるようになりました。

例えば、

> procurement_df
# A tibble: 33,774 × 8
   procurement_item_no procurement_item_name                                 award_date award_price ministry_code bidding_method_code supplier_name corporate_number
   <chr>               <chr>                                                 <date>           <dbl> <chr>         <chr>               <chr>         <chr>           
 1 0000000000000443261 令和6年度 福岡SMC九州南部技術管理支援業務請負    2025-04-01     2961000 S1            8002010             鹿児島綜合警 7340001000891   
 2 0000000000000478587 中央合同庁舎第3号館で使用するガス                    2025-04-01    15268657 S1            8002010             ENEOS … 6010001237378   
 3 0000000000000478607 札幌管区気象台庁舎他で使用する電気高圧の調達      2025-04-01    20187469 S4            8002010             王子伊藤忠 5010401116372   
 4 0000000000000478615 札幌管区気象台管内で使用する電気低圧の調達        2025-04-01    16983725 S4            8002010             リエスパワー 3013301039380   
 5 0000000000000479004 札幌運輸支局他で使用する電気の購入                    2025-04-01    24096146 S1            8002010             王子伊藤忠 5010401116372   
 6 0000000000000479349 神戸航空交通管制部で使用する電気の購入                2025-04-01    90612769 S1            8002010             中部電力ミラ 2180001135973   
 7 0000000000000479518 令和7年度マイナポータルアプリの運用保守業務一式    2025-04-01   142080000 W1            8002040             バルテス株式 3120001253929   
 8 0000000000000479690 国土技術政策総合研究所横須賀庁舎で使用する電気    2025-04-01    17992726 S1            8002010             ゼロワットパ 1040001089656   
 9 0000000000000479757 松山河川国道事務所庁舎で使用する電気                  2025-04-01    15739228 S1            8002010             四国電力株式 9470001001933   
10 0000000000000479947 令和7年度東北農政局岩手県拠点愛宕庁舎ほか4庁舎で使 2025-04-01     6924293 Q1            8002010             ゼロワットパ 1040001089656   
# ℹ 33,764 more rows
# ℹ Use `print(n = ...)` to see more rows

の感じのデータが得られます。

今回の分析では、この約3.4万件の案件について、

$$
Y = \text{落札価格}
$$

$$
D = \text{調達案件名称}
$$

$$
X =
{
\text{府省コード},
\text{入札方式コード}
}
$$

という構造を作っていきます。

ここで重要なのは、データ取得の時点で特別な登録や認証を必要としないことです。

読者も同じコードを実行すれば、同じ公式データを取得できます。

つまり、今回の分析は最初から最後まで、

誰でも取得できる政府のオープンデータを使って再現可能な形で進める

ことができます。

まず、$X$ と $Y$ の間に本当に関係があるのかを確認しましょう。ここにほとんど関係がなければ、わざわざ構造化変数の影響を取り除いてテキストを分析してもあまり面白くありません。そこで、落札価格の対数を府省コードで説明するモデルと、落札価格の対数を府省コードと入札方式コードで説明するモデルをそれぞれ推定します。

m_pm <- procurement_df |>
  dplyr::mutate(
    log_award_price = log(award_price),
    ministry_code = factor(ministry_code),
    bidding_method_code = factor(bidding_method_code)
  ) |>
  lm(
    log_award_price ~ ministry_code,
    data = _
  )


m_pmb <- procurement_df |>
  dplyr::mutate(
    log_award_price = log(award_price),
    ministry_code = factor(ministry_code),
    bidding_method_code = factor(bidding_method_code)
  ) |>
  lm(
    log_award_price ~
      ministry_code +
      bidding_method_code,
    data = _
  )

カテゴリが大変多いため、すべての係数を羅列すると記事としての可読性が下がります。そこで、計量経済学の先生から破門処分を受ける覚悟で、ここでは統計的に有意になった係数の数だけを確認します。

> broom::tidy(m_pm) |>
  dplyr::filter(
    term != "(Intercept)"
  ) |>
  dplyr::mutate(
    variable = dplyr::case_when(
      stringr::str_detect(term, "^ministry_code") ~ "府省",
      stringr::str_detect(term, "^bidding_method_code") ~ "入札方式"
    )
  ) |>
  dplyr::group_by(variable) |>
  dplyr::summarise(
    n = dplyr::n(),
    significant = sum(p.value < 0.05)
  )
# A tibble: 1 × 3
  variable     n significant
  <chr>    <int>       <int>
1 府省        43          22

府省コード + 入札方式コードに関しては:

> broom::tidy(m_pmb) |>
  dplyr::filter(
    term != "(Intercept)"
  ) |>
  dplyr::mutate(
    variable = dplyr::case_when(
      stringr::str_detect(term, "^ministry_code") ~ "府省",
      stringr::str_detect(term, "^bidding_method_code") ~ "入札方式"
    )
  ) |>
  dplyr::group_by(variable) |>
  dplyr::summarise(
    n = dplyr::n(),
    significant = sum(p.value < 0.05)
  )
# A tibble: 2 × 3
  variable     n significant
  <chr>    <int>       <int>
1 入札方式    11           8
2 府省        43          28

ここでは個々の係数について過剰な解釈はしません。

府省コードだけでも43個の係数のうち22個、さらに入札方式を加えると府省では43個中28個、入札方式では11個中8個が5%水準で統計的に有意になっています。

そもそも、府省によって調達するものが違い、入札方式によって扱われる案件も違うことを考えれば、これらと落札価格に関係があること自体はそれほど意外ではありません。

今回重要なのは、

落札価格と関連する情報を、すでに「府省コード」「入札方式コード」という構造化変数として持っている

ということです。

したがって、案件名のテキストから落札価格と関連する単語を探すときに、これらの影響を無視するわけにはいきません。

例えば、ある府省が高額な案件を多く扱い、その府省特有の単語が案件名にも頻繁に登場するのであれば、単純なテキスト分析ではその単語が「高い落札価格と関連する単語」として発見される可能性があります。

しかし、それでは先ほどのECサイトで、

東京都の商品はよく売れる → 東京都の商品には「新宿」と書いてある → 「新宿」が売上を説明する重要な単語だ!

と言っているのと同じです。

それはもう知っています。

そこで次からは、府省や入札方式といった既存の構造化情報を考慮しながら、それでも落札価格と関連しているテキスト上の特徴を探していきます。

テキストデータを前処理する

続いて、調達案件名称をテキスト分析に使える形へ変換していきます。

今回のデータには、例えば次のような案件名が入っています。

令和6年度 福岡SMC(九州南部)技術管理支援業務請負

このままでは単語単位の分析ができないため、まず形態素解析を行います。

今回は RMeCab を使います。

名詞だけを残す

前処理には、次の非常に単純な関数を使います。

mecabbing <- function(text){
  text |>
    RMeCab::RMeCabC() |> 
    purrr::keep(\(x) names(x) == "名詞") |>
    unlist(use.names = FALSE) |>
    stringr::str_c(collapse = " ")
}

やっていることは単純で、

  1. RMeCab::RMeCabC() で形態素解析する
  2. 品詞が「名詞」のものだけを残す
  3. それらをスペースで連結する

だけです。

例えば、

> RMeCab::RMeCabC(procurement_df$procurement_item_name[2000]) |> unlist()
              名詞               名詞               名詞               名詞               名詞               名詞               名詞               名詞 
              "令"               "和"               "7"             "年度" "鹿児島空港事務所"             "電話"             "交換"               "機" 
              名詞               名詞 
            "保守"             "作業" 

のようにすると、案件名が形態素に分解され、それぞれに品詞が付与されます。

今回はその中から名詞だけを取り出します。

なぜ名詞だけなのか

ここで、

なぜ名詞だけ残すのか?

という疑問があると思います。

実は、ここには壮大な理論的背景があるわけではありません。

以前、自然言語処理を専門にしていた元同僚から、

MeCabを使うなら、とりあえず名詞だけ残すと結構うまくいきますよ

という趣旨のアドバイスをもらったことがあります。

その後、個人的な分析や会社のプロジェクトでもこの方法を何度か使ってきましたが、経験的にはかなり使いやすい前処理でした。

もちろん、

日本語のテキスト分析では必ず名詞だけを使うべきである

という意味ではありません。

分析対象によっては動詞や形容詞が非常に重要でしょうし、品詞の細分類を利用したり、固有名詞だけを残したりする方法も考えられます。

今回はあくまで、私が実務や個人的な分析で使ってきて、経験的に扱いやすかった単純なルールとして名詞だけを残します。

もちろん、本記事でも、これまで私が担当してきた分析でも、この前処理を採用するかどうかを最終的に判断したのは私自身です。元同僚から教えてもらった方法ではありますが、分析上の判断や、その結果に対する責任まで元同僚にあるわけではありません。もしこの前処理が不適切な分析結果につながったのであれば、その責任は分析者である私にあります。

今回はあえて、それ以上あまり掃除しない

そして今回は、テキストに対してこれ以上の複雑なクリーニングをほとんど行いません。

実際のテキスト分析であれば、

  • 数字や記号をどう扱うか
  • 年度表記をどう扱うか
  • 表記揺れをどう統一するか
  • 一般的すぎる単語を除外するか
  • 固有名詞をどう扱うか
  • 独自のストップワードを作るか

など、まだまだ考えられることがあります。

しかし、このあたりは分析対象によってかなり事情が変わります。

例えば、あるデータでは数字が単なるノイズでも、別のデータでは「5年」「第3四半期」「100kW」のような表現そのものが重要な情報かもしれません。

そして、前処理を丁寧にやり始めると、それだけで一本の記事が書けますw

今回はそこが主役ではありません。

この記事で見たいのは、

既存の構造化変数を考慮する前と後で、目的変数と関連するテキスト上の特徴がどう変わるのか?

です。

そのため、前処理を作り込みすぎず、

名詞だけ取り出して、とりあえずモデルに投げ込んだら何が起こるのか

を見てみます。

これはある意味、今回の分析の面白いところでもあります。

大量のドメイン知識を使ってストップワード辞書を作り、結果がきれいになるまで前処理を調整したうえでモデルを見せるのではなく、かなり素朴な前処理からスタートします。

それでも、構造化変数を入れる前後で意味のある違いが見えてくるのでしょうか。

まずは案件名すべてに、この関数を適用していきます。

future::plan(future::multisession(workers = 16))
procurement_mecab_df <- procurement_df |>
  dplyr::mutate(
    procurement_item_name_mecabbed = procurement_item_name |>
      furrr::future_map_chr(
        \(x){
          mecabbing(x)
        },
        .progress = TRUE
      )
  )

最後に、MeCabで処理したデータを取り出して、文書行列を作成します:

procurement_dfm <- procurement_mecab_df |>
  dplyr::pull(procurement_item_name_mecabbed) |>
  quanteda::phrase() |>
  quanteda::tokens() |>
  quanteda::tokens_ngrams(1:2) |>
  quanteda::dfm() |>
  quanteda::dfm_trim(min_termfreq = 50)

DMRを推定する

いよいよ DMRを推定していきます。

今回知りたいのは、単純に

どの単語が落札価格と関連しているのか?

だけではありません。

前節までで見たように、落札価格は府省や入札方式と関連しています。そして当然、府省や入札方式によって案件名に登場する単語も異なると考えられます。

そこで今回は、投入する構造化変数を段階的に増やした3つのDMRを推定します。

モデル 落札価格 府省コード 入札方式コード
m_dmr_naive
m_dmr_ministry
m_dmr_ministry_bidding

つまり、1つ目のモデルでは落札価格と単語の関係をそのまま見ます。

2つ目では府省コードを追加し、府省の違いを考慮してもなお落札価格と関連する単語を探します。

そして3つ目では入札方式も追加し、府省と入札方式の両方を考慮したあとにも残る落札価格と単語の関係を見ます。

この3つを比較することで、構造化情報を追加するにつれて「落札価格と関連する単語」がどのように変化していくのかを確認します。

まずは落札価格だけで推定する

最初は最も単純なモデルです。

落札価格は対数変換したあと標準化し、それだけを共変量としてDMRを推定します。

cl <- parallel::makeCluster(16)

m_dmr_naive <- distrom::dmr(
  cl,
  counts = procurement_dfm,
  covars = procurement_mecab_df |>
    dplyr::mutate(
      log_price_std = (log(award_price) - mean(log(award_price)))/sd(log(award_price))
    ) |>
    model.matrix(~ -1 + log_price_std, data = _),
  gamma = 1,
  standardize=FALSE,
  verb = TRUE
)

parallel::stopCluster(cl)

ここではまだ府省も入札方式も教えていません。

したがって、このモデルから得られるのは、

案件名にその単語が登場することと、落札価格が高い・低いことがどのように関連しているか

という、かなり素朴な関係です。

もし高額案件を多く扱う特定の府省でしか使われない単語があれば、その単語も高い落札価格と関連する特徴として拾われる可能性があります。

先ほどのECサイトの例でいえば、まだ「東京都」という構造化情報をモデルに渡さず、「新宿」という単語と購入の関係を探している状態です。

府省コードを追加する

次に、府省コードを追加します。

cl <- parallel::makeCluster(16)

m_dmr_ministry <- distrom::dmr(
  cl,
  counts = procurement_dfm,
  covars = procurement_mecab_df |>
    dplyr::mutate(
      log_price_std = (log(award_price) - mean(log(award_price)))/sd(log(award_price))
    ) |>
    model.matrix(~ -1 + log_price_std + ministry_code, data = _),
  varweight = c(
    1,
    rep(1/20, 44)
  ),
  gamma = 1,
  standardize=FALSE,
  verb = TRUE
)

parallel::stopCluster(cl)

ここでは、落札価格だけでなく府省コードも同時にモデルへ渡しています。

そのため、ここで注目したいのは、

府省による案件名の違いをモデルに与えたあとで、落札価格と関連する単語として何が残るのか?

です。

ECサイトの例に戻れば、

$$
\text{東京都}
\rightarrow
\text{新宿}
$$

という情報をモデルに与えたうえで、それでも「新宿」と購入の関係が残るのかを見ることに対応します。

さらに入札方式も追加する

最後に、府省コードに加えて入札方式コードも追加します。

cl <- parallel::makeCluster(16)

m_dmr_ministry_bidding <- distrom::dmr(
  cl,
  counts = procurement_dfm,
  covars = procurement_mecab_df |>
    dplyr::mutate(
      log_price_std = (log(award_price) - mean(log(award_price)))/sd(log(award_price))
    ) |>
    model.matrix(~ -1 + log_price_std + ministry_code + as.factor(bidding_method_code), data = _),
  varweight = c(
    1,
    rep(1/20, 55)
  ),
  gamma = 1,
  standardize=FALSE,
  verb = TRUE
)

parallel::stopCluster(cl)

これが今回もっとも多くの構造化情報を考慮したモデルです。

ここでは、

$$
\text{府省}
+
\text{入札方式}
$$

によって説明されるテキスト上の違いをモデルに与えたうえで、それでも落札価格と関連している単語を探します。

したがって、今回最も見たいのは、単純な m_dmr_naive で上位に出てきた単語が、

m_dmr_naive
        ↓
m_dmr_ministry
        ↓
m_dmr_ministry_bidding

と構造化情報を追加していくことで、どのように変化するのかです。

ハイパーパラメータについて

なお、コード中では、

gamma = 1
standardize = FALSE

を指定し、府省や入札方式を追加したモデルでは、

varweight = c(
  1,
  rep(1/20, ...)
)

という設定を使っています。

これらは今回の政府調達データを見ながら、結果がきれいになるように私が独自にチューニングした値ではありません。

今回の設定は、DMRの提案者である Matt Taddy らが公開している Yelp データを用いた分析コード cv-fit.R の設定を参考にしています。

特に今回重要なのが varweight です。

府省や入札方式を追加したモデルでは、

1

を落札価格(興味の中心となる変数)に、

1/20

をその他の変数に設定しています。

つまり、すべての説明変数をまったく同じ重みのかけ方で扱うのではなく、今回関心のある落札価格と、調整のために投入している構造化変数で重みを変えています。

なお、gamma = 1 は Gamma Lasso の設定であり、standardize = FALSE はモデル内部で説明変数を改めて標準化しない設定です。DMRの詳細と理論に関する説明は Taddy(2015)をご参照ください。distromgamlr を利用してDMRを推定しており、Gamma Lasso 自体については Taddy(2017)などで詳しく説明されています。

今回はこれらのハイパーパラメータそのものを比較することが目的ではないため、ここでは既存の公開分析コードの設定を参考に固定し、

構造化情報をモデルに追加すると、落札価格と関連して見える単語がどう変化するのか

に集中します。

それでは、実際に推定された単語の係数を見てみましょう。

推定結果を確認する

モデルの推定が終わったので、いよいよ結果を確認していきます。

今回特に見たいのは、それぞれの単語について推定された log_price_std の係数です。

つまり、

落札価格が高い案件では、どの単語が相対的に多く登場するのか?

そして何より、

府省や入札方式をモデルに追加すると、その関係がどう変化するのか?

を3つのモデルで比較します。

ただし、distrom::dmr() の推定結果は、今回のように数千単語の係数をまとめて比較するには、そのままでは少し扱いにくい形になっています。

そこで、各単語について推定された係数を取り出し、

text covariate | word | coef

という縦長のデータフレームに変換する関数を用意します。

extract_dmr_coef <- function(model) {
  B <- lapply(
    model,
    coef
  )

  failures <- vapply(
    B,
    is.null,
    logical(1)
  )

  for (i in which(failures)) {
    B[[i]] <- Matrix::Matrix(
      0,
      nrow = nrow(B[[which(!failures)[1]]]),
      ncol = 1,
      sparse = TRUE
    )
  }

  bx <- unlist(
    lapply(B, \(b) b@x)
  )

  bi <- unlist(
    lapply(B, \(b) b@i)
  )

  bp <- c(
    0,
    cumsum(
      unlist(
        lapply(B, \(b) b@p[-1])
      )
    )
  )

  Matrix::sparseMatrix(
    i = bi + 1,
    p = bp,
    x = bx,
    dims = c(
      nrow(B[[1]]),
      length(B)
    ),
    dimnames = list(
      rownames(B[[1]]),
      names(B)
    )
  ) |>
    as.matrix() |> 
    as.data.frame() |> 
    tibble::rownames_to_column(var = "covariate") |> 
    tibble::tibble() |> 
    tidyr::pivot_longer(!covariate, names_to = "word", values_to = "coef")
}

まずは推定結果をざっと眺めてみる

先ほど作った関数を使って、3つのモデルから係数を取り出します。

coef_naive <- extract_dmr_coef(m_dmr_naive)

coef_ministry <- extract_dmr_coef(m_dmr_ministry)

coef_ministry_bidding <- extract_dmr_coef(m_dmr_ministry_bidding)

続いて、それぞれの共変量について、

  • 係数が大きい単語
  • 係数が小さい単語

の順に並べて確認してみます。

係数が0になっているものについては、見やすさのために "NULL" と表示しています。

> coef_naive |>
  dplyr::filter(covariate != "intercept") |>
  split(~ covariate) |>
  purrr::map(
    \(this_df){

      high <- this_df |>
        dplyr::mutate(
          word = dplyr::case_when(
            coef == 0 ~ "NULL",
            TRUE ~ word
          )
        ) |>
        dplyr::arrange(-coef) |>
        dplyr::select(word) |>
        `colnames<-`(stringr::str_c(this_df$covariate, "_high"))

      low <- this_df |>
        dplyr::mutate(
          word = dplyr::case_when(
            coef == 0 ~ "NULL",
            TRUE ~ word
          )
        ) |>
        dplyr::arrange(coef) |>
        dplyr::select(word) |>
        `colnames<-`(stringr::str_c(this_df$covariate, "_low"))

      cbind(high, low)
    }
  ) |>
  dplyr::bind_cols() |>
  dplyr::glimpse()
Rows: 1,659
Columns: 2
$ log_price_std_high <chr> "設計_開発", "システム_更改", "更改", "整備_保育", "管内_電気", "森林_環境", "保育_間伐", "間伐", "環境_保全", "一式_令", "保育", "保全_…
$ log_price_std_low  <chr> "重油_供給", "3_四半期", "灯油_供給", "4_四半期", "四半期", "白_灯油", "供給_単価", "a_重油", "年度_単価", "", "灯油", "重油", "年度
coef_ministry |>
  dplyr::filter(covariate != "intercept") |>
  split(~ covariate) |>
  purrr::map(
    \(this_df){

      high <- this_df |>
        dplyr::mutate(
          word = dplyr::case_when(
            coef == 0 ~ "NULL",
            TRUE ~ word
          )
        ) |>
        dplyr::arrange(-coef) |>
        dplyr::select(word) |>
        `colnames<-`(stringr::str_c(this_df$covariate, "_high"))

      low <- this_df |>
        dplyr::mutate(
          word = dplyr::case_when(
            coef == 0 ~ "NULL",
            TRUE ~ word
          )
        ) |>
        dplyr::arrange(coef) |>
        dplyr::select(word) |>
        `colnames<-`(stringr::str_c(this_df$covariate, "_low"))

      cbind(high, low)
    }
  ) |>
  dplyr::bind_cols() |>
  dplyr::glimpse()
Rows: 1,659
Columns: 90
$ log_price_std_high   <chr> "整備_保育", "森林_環境", "保育_間伐", "間伐", "保全_整備", "間伐_活用", "保育", "活用_型", "事業_保育", "環境_保全", "地区_保全", "シ…
$ log_price_std_low    <chr> "方式_見積", "見積", "カウンター_方式", "重油_供給", "年度_単価", "船用_燃料", "水産庁_漁業", "調査_取締", "漁業_調査", "オープン_カウ
$ ministry_codeA1_high <chr> "ほか_購入", "借入", "伝送", "集中", "外_4", "3月", "電子_化", "ほか_3", "外_3", "服", "整備_工事", "昇降_機", "外_5", "放送", "…
$ ministry_codeA1_low  <chr> "調査", "実施", "研究", "運営", "支援", "", "情報", "年度", "", "令_和", "", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NU
$ ministry_codeB1_high <chr> "点検_保守", "2_件", "回_目", "給水", "その他_工事", "昇降_機", "売", "売_払", "服", "ホームページ", "1_件", "ゲート", "町", "外_5"
$ ministry_codeB1_low  <chr> "支援", "年度", "運営", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeC1_high <chr> "入力", "式", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", 
$ ministry_codeC1_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeD1_high <chr> "地方裁判所", "地方裁判所_庁舎", "地裁", "支部_庁舎", "簡易_裁判所", "合同庁舎_等", "支部", "高等", "裁判所", "庁舎_機械", "エレベータ…
$ ministry_codeD1_low  <chr> "研究", "広報", "", "促進", "技術", "活用", "強化", "運営_業務", "事業", "開催", "7", "推進", "調査_研究", "補助", "育成", "導入", "
$ ministry_codeE1_high <chr> "機_更新", "各種", "電気_需給", "通訳", "検討_調査", "効率_化", "リコー", "需給_契約", "ファイル", "外壁", "等_管理", "等_工事", "効率…
$ ministry_codeE1_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeF1_high <chr> "国家", "報告_書", "分析_業務", "電子_化", "実現", "", "", "携帯", "", "移転", "問題", "セミナー", "/", "購入_等", "等_一式", "
$ ministry_codeF1_low  <chr> "支援", "和", "令_和", "令", "等_業務", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeG1_high <chr> "展示", "衛星", "管制", "演習", "ほか_購入", "循環", "若年", "政府", "取組", "国土", "通訳", "推進_調査", "リスク", "人材_育成", "情報…
$ ministry_codeG1_low  <chr> "", "", "処理", "診断", "設置", "印刷", "", "電話", "会議", "定期", "交換", "監視", "改修", "工事", "", "", "サービス", "
$ ministry_codeH1_high <chr> "電話_交換", "データベース", "照明", "中央", "支援_システム", "移行", "変更", "自動車_運行", "椅子", "審査", "運行_管理", "運行", "事…
$ ministry_codeH1_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeJ1_high <chr> "年度_原子力", "年度_経済", "海洋", "事務所_車両", "解消", "障害_者", "就職", "業務_艇", "港湾_事務所", "研究_開発", "通信_設備", "
$ ministry_codeJ1_low  <chr> "試験", "移行", "自動", "機器_等", "変更", "ソフトウェア", "3", "建築", "文書", "二", "受付", "一", "診断", "内", "情報_提供", "診断_…
$ ministry_codeJ2_high <chr> "区域", "整備_工事", "草刈", "予定", "その他_工事", "", "循環", "京都", "貨物", "更新_工事", "", "売_払", "ほか_購入", "庁舎_ほか"…
$ ministry_codeJ2_low  <chr> "支援", "実施", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeJ3_high <chr> "適正", "執務_室", "報告_書", "講習", "事業_者", "1_", "中小", "事務_用品", "本局", "ソフトウェア_ライセンス", "年度_事務", "解析",…
$ ministry_codeJ3_low  <chr> "実施", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeJ5_high <chr> "警察", "管区_警察", "県_情報", "電子_可", "通信_部", "管区", "情報_通信", "", "ほか_購入", "通信_施設", "5_", "神奈川", "伝送", …
$ ministry_codeJ5_low  <chr> "促進", "運用_保守", "", "システム_運用", "監視", "", "強化", "地域", "運行", "実施", "運用", "運行_管理", "関連", "構築", "調査",…
$ ministry_codeJ6_high <chr> "報告_書", "モニタリング", "搬送", "各種", "ライセンス_購入", "貨物", "検知", "", "回収", "化_対策", "者_用", "業務_一式", "事業_者"…
$ ministry_codeJ6_low  <chr> "運営", "", "施設", "年度", "7_年度", "", "令_和", "自動車", "電話", "工事", "", "", "和_7", "", "NULL", "NULL", "NULL", "
$ ministry_codeJ7_high <chr> "食品", "区分_3", "電子_化", "業務_公告", "表彰", "本部", "システム_更改", "調査_票", "ため_調査", "災害_時", "状況_等", "推進_調査",
$ ministry_codeJ7_low  <chr> "処理", "改修", "診断", "施設", "事務", "設置", "1", "会議", "用", "購入", "更新", "工事", "年度", "回", "NULL", "NULL", "NULL", "NUL…
$ ministry_codeJ8_high <chr> "広報_業務", "団体", "制度_等", "取組", "保護", "認定", "公共", "移転", "コンテンツ", "事業_者", "委員", "訓練", "会_運営", "開発_等",…
$ ministry_codeJ8_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeJ9_high <chr> "分析_業務", "購入_等", "規制", "事業_者", "電話_交換", "手続", "海外", "翻訳", "", "年度_自動車", "訓練", "実施_業務", "労働_者", "
$ ministry_codeJ9_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeJA_high <chr> "家庭", "委託_一式", "事業_一式", "取組", "一式_令", "広報_業務", "調査_票", "局_業務", "権利", "認定", "制度_等", "実現", "情報_発信"
$ ministry_codeJA_low  <chr> "6", "施設", "物", "処理", "等_購入", "用", "室", "設置", "国際", "更新", "機器", "購入", "技術", "監視", "保守", "庁舎", "NULL", "NU…
$ ministry_codeK1_high <chr> "0042_-", "0042", "-_0042", "07_-", "08_-", "0049", "-_0049", "0049_-", "07", "08", "調査_請負", "2_号館", "うち", "電波", "研究_請負
$ ministry_codeK1_low  <chr> "ライセンス", "廃棄", "官", "管理_支援", "購入_単価", "賃貸借_保守", "廃棄_物", "保安", "診断_業務", "一", "業務_単価", "支援_システム…
$ ministry_codeK3_high <chr> "学校", "記念", "操作", "分析_業務", "講習", "衛星", "規模", "統計_調査", "伝送", "表彰", "搬送", "通信_設備", "消防", "全国", "災害_
$ ministry_codeK3_low  <chr> "6", "3", "診断", "等_購入", "室", "事務", "管理_業務", "印刷", "設置", "電話", "工事", "定期", "2", "購入", "使用", "NULL", "NULL"
$ ministry_codeL1_high <chr> "法務局_地図", "型_法務局", "東京_出入国", "在留_管理", "刑務所_等", "鑑別", "少年_鑑別", "鑑別_所", "収容_者", "少年_刑務所", "出入国…
$ ministry_codeL1_low  <chr> "開催", "強化", "安全", "可能", "プログラム", "動向", "受付", "データベース", "向上", "報告", "海外", "支援_事業", "10", "情報_収集", …
$ ministry_codeL2_high <chr> "法務_合同庁舎", "法務_総合", "検察庁", "地方_検察庁", "法務", "合同庁舎_ほか", "支部_庁舎", "合同庁舎_等", "総合_庁舎", "総合_研究所"…
$ ministry_codeL2_low  <chr> "支援", "運営", "", "研究", "運用", "作成", "会議", "外国", "広報", "", "", "", "分析", "促進", "調査_業務", "国際", "センター
$ ministry_codeL4_high <chr> "年度_自動車", "トナーカートリッジ", "単価_契約", "単価", "自動車", "トナーカートリッジ_等", "等_購入", "年間", "供給_単価", "近畿", "…
$ ministry_codeL4_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeM1_high <chr> "報告_書", "通訳", "", "北陸", "システム_更改", "貨物", "各種", "リスク", "協力", "統合_管理", "化学", "オフィス", "日本", "記念", "
$ ministry_codeM1_low  <chr> "処理", "等_購入", "システム_運用", "交換", "技術", "研究", "清掃", "導入", "育成", "施設", "補助", "管理_業務", "自動車", "調査", "体…
$ ministry_codeN1_high <chr> "監視_艇", "合同_宿舎", "財務省", "税関", "処分_等", "", "年度_国有", "その他_工事", "ほか_1", "草刈", "探知", "07", "管理_センター"…
$ ministry_codeN1_low  <chr> "", "促進", "強化", "育成", "体制", "オンライン", "可能", "動向", "向上", "デジタル", "化_調査", "データベース", "ソフトウェア", "
$ ministry_codeN2_high <chr> "確定_申告", "国税", "税務署", "業務_区分", "国税局", "所得_税", "国税局_管内", "申告_期", "区分_1", "委託_区分", "税_確定", "業務_グ…
$ ministry_codeN2_low  <chr> "研究", "", "運用_保守", "強化", "等_調査", "調査_研究", "運行", "実態_調査", "育成", "運行_管理", "導入", "体制", "検討", "安全", "
$ ministry_codeO1_high <chr> "学校", "高等", "省", "研究所", "展示", "科学", "草刈", "調査_票", "認定", "事業_一式", "研", "供給_一式", "使用_電力", "検知", "権利"
$ ministry_codeO1_low  <chr> "3", "設置", "電話", "交換", "促進", "提供", "機能", "監視", "運営_業務", "自動車", "購入", "単価_契約", "サービス", "検査", "使用_電…
$ ministry_codeO2_high <chr> "", "", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "N
$ ministry_codeO2_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeP1_high <chr> "労働_局", "監督_署", "安定_所", "基準_監督", "労働_基準", "東京_労働", "中高年", "年度_中高年", "活躍_応援", "応援_プロジェクト", "中…
$ ministry_codeP1_low  <chr> "運行_管理", "自動車_運行", "年版", "等_請負", "高度", "等_清掃", "資料_作成", "", "運行", "システム_開発", "ソフトウェア_ライセンス
$ ministry_codeP2_high <chr> "維持_管理", "維持", "会", "内", "警備_業務", "自動車_運行", "運行_管理", "運行", "清掃", "警備", "使用_電気", "一般", "管理_業務", "…
$ ministry_codeP2_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeQ1_high <chr> "農林_水産", "農政_局", "農政", "農林", "検疫", "検疫_所", "農地", "うち", "水産", "年度_国有", "動物", "国営", "リスク", "排水_ポンプ
$ ministry_codeQ1_low  <chr> "開催", "受付", "一", "電話_交換", "構内", "執務", "等_整備", "運転", "輸送", "購入_等", "清掃_等", "沖縄", "電", "案内", "業務_委託",
$ ministry_codeQ2_high <chr> "方式_見積", "整備_保育", "森林_環境", "保育_間伐", "保全_整備", "間伐_活用", "見積", "事業_保育", "号_物件", "砕石_購入", "森林_整備"
$ ministry_codeQ2_low  <chr> "研究", "外国", "電話", "研修", "促進", "広報", "国際", "強化", "開催", "法", "作成_業務", "情報_システム", "補助", "運行", "開発", "…
$ ministry_codeQ3_high <chr> "船用_燃料", "水産庁_漁業", "調査_取締", "漁業_調査", "取締_船用", "水産庁", "漁業", "", "うち", "油_購入", "船用", "水産", "購入_令
$ ministry_codeQ3_low  <chr> "自動車", "実施", "物", "印刷", "処理", "室", "研究", "診断", "6", "事務", "等_購入", "作成", "1", "定期", "会議", "5", "単価_契約"
$ ministry_codeR1_high <chr> "近畿_経済", "産業_保安", "産業_局", "年度_経済", "産業_省", "年度_エネルギー", "広報_事業", "委託_費", "事業_費", "経済_産業", "年度_…
$ ministry_codeR1_low  <chr> "機器_等", "維持_管理", "警備_業務", "等_保守", "データベース", "装置", "3", "中央", "点検_業務", "保守_等", "クラ", "ウド", "クラ_ウ
$ ministry_codeR3_high <chr> "知_財", "財", "知", "産業_省", "分野", "簡易_型", "台_賃貸借", "報告_書", "予定", "通訳", "中小", "財産", "年度_産業", "モデル", "検…
$ ministry_codeR3_low  <chr> "施設", "診断", "印刷", "", "事務", "設置", "工事", "更新", "定期", "研修", "交換", "強化", "", "設計", "", "調査_業務", "センタ
$ ministry_codeS1_high <chr> "整備_局", "地方_整備", "河川_事務所", "河川_国道", "部_管内", "製造_据付", "工事_外", "本局_7", "砂防", "航空_交通", "文_観測", "水_…
$ ministry_codeS1_low  <chr> "電気_供給", "作業_等", "セミナー", "周知", "什器", "通知", "自動車_運行", "什器_等", "研究", "", "", "調査_研究", "什器_購入", "
$ ministry_codeS2_high <chr> "報告_書", "資源", "服", "画像", "東", "解析", "運転", "派遣_業務", "サーバ", "紙", "航空機", "海外", "1", "印刷_製本", "ほか_2", "文…
$ ministry_codeS2_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeS3_high <chr> "通訳", "統計_調査", "分析_業務", "認定", "回収", "等_作成", "協力", "全国", "等_管理", "登録", "案内", "会場", "印刷_製本", "製本", "
$ ministry_codeS3_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeS4_high <chr> "地方_気象台", "気象台", "丸", "管区", "回_目", "取付", "観測_装置", "学校", "気象", "衛星", "伝送", "ほか_購入", "管内_使用", "検知",
$ ministry_codeS4_low  <chr> "8", "和_8", "物", "的", "8_年度", "印刷", "広報", "会", "システム_運用", "年度", "和", "令_和", "令", "賃貸借", "4", "強化", "対…
$ ministry_codeS5_high <chr> "買_入", "点_買", "型_巡視", "巡視_艇", "四_管区", "本部__", "軽油_免税", "管区_海上", "保安_本部", "海上_保安", "入_単価", "式_買", "
$ ministry_codeS5_low  <chr> "購入", "的", "等_購入", "広報", "促進", "システム_運用", "活用", "対応", "年", "運行", "育成", "事務_用", "実態_調査", "体制", "運行_…
$ ministry_codeT1_high <chr> "国立_公園", "方式_]", "[_総合", "業務_[", "繰越", "公園", "業務_総合", "落札", "落札_方式", "鳥獣", "評価_落札", "総合_評価", "捕獲",…
$ ministry_codeT1_low  <chr> "等_購入", "印刷", "庁舎", "ライセンス", "事務_用", "プログラム", "椅子", "機器_等", "業務_請負", "等_保守", "業務_委託", "3", "診断_
$ ministry_codeT2_high <chr> "委託_費", "年度_原子力", "原子力", "体", "規制", "リスク", "モニタリング", "影響", "執務_室", "性能", "事業_委託", "災害_時", "物質",
$ ministry_codeT2_low  <chr> "自動車", "改修", "診断", "作成", "3", "事務", "印刷", "5", "システム_運用", "会議", "2", "使用", "交換", "促進", "機能", "情報_シ…
$ ministry_codeU1_high <chr> "局_7", "(_7", "陸_装", "装_研", "住宅_防音", "防衛_局", "防音_事業", "支援_役務", "7_)", "防音", "飛行場", "防衛", "下北", "", "
$ ministry_codeU1_low  <chr> "回", "実態_調査", "保守_業務", "実施_業務", "自動車_運行", "業務_請負", "運用_保守", "二", "一", "8_年度", "和_8", "情報_提供", "会",
$ ministry_codeV1_high <chr> "宮城", "財務", "区域", "システム_更改", "東北", "年度_産業", "福島", "ホームページ", "等_作成", "書類", "会_運営", "作成_等", "自動車…
$ ministry_codeV1_low  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeW1_high <chr> "年度_デジタル", "2_号館", "借入", "ため_調査", "政府", "執務_室", "合同庁舎_2", "分野", "出入国_在留", "納付", "効率_化", "不動産",…
$ ministry_codeW1_low  <chr> "", "処理", "", "外国", "使用", "", "電話", "", "使用_電気", "育成", "印刷", "定期", "交換", "体制", "電気", "センター", "",
coef_ministry_bidding |>
  dplyr::filter(covariate != "intercept") |>
  split(~ covariate) |>
  purrr::map(
    \(this_df){

      high <- this_df |>
        dplyr::mutate(
          word = dplyr::case_when(
            coef == 0 ~ "NULL",
            TRUE ~ word
          )
        ) |>
        dplyr::arrange(-coef) |>
        dplyr::select(word) |>
        `colnames<-`(stringr::str_c(this_df$covariate, "_high"))

      low <- this_df |>
        dplyr::mutate(
          word = dplyr::case_when(
            coef == 0 ~ "NULL",
            TRUE ~ word
          )
        ) |>
        dplyr::arrange(coef) |>
        dplyr::select(word) |>
        `colnames<-`(stringr::str_c(this_df$covariate, "_low"))

      cbind(high, low)
    }
  ) |>
  dplyr::bind_cols() |>
  dplyr::glimpse()
Rows: 1,659
Columns: 112
$ `as.factor(bidding_method_code)8002010_high` <chr> "収穫_調査", "収穫", "署_収穫", "施設_使用", "自動車_運行", "地区_建設", "使用_電力", "運行_管理", "型_法務局"
$ `as.factor(bidding_method_code)8002010_low`  <chr> "方式_見積", "カウンター_方式", "オープン_カウンター", "オープン", "見積", "カウンター", "東京航空局", "東京航…
$ `as.factor(bidding_method_code)8002020_high` <chr> "", "発生", "", "観測_装置", "入札", "公園_事務所", "用_自動車", "簡易_裁判所", "", "荒川", "長野", "
$ `as.factor(bidding_method_code)8002020_low`  <chr> "等_工事", "ファイル", "京都", "仙台", "1_4", "交換_作業", "年度_京都", "市", "空港_事務所", "昇降_機", "10"
$ `as.factor(bidding_method_code)8002040_high` <chr> "年度_若年", "型_請負", "請負_総合", "整備_保育", "車両_管理", "間伐_活用", "活用_型", "台_賃貸借", "業務_総合…
$ `as.factor(bidding_method_code)8002040_low`  <chr> "見積", "方式_見積", "交換_作業", "カウンター", "カウンター_方式", "オープン_カウンター", "1_4", "", "ほか
$ `as.factor(bidding_method_code)8002050_high` <chr> "医療", "製造", "用", "購入", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", 
$ `as.factor(bidding_method_code)8002050_low`  <chr> "京都", "局_業務", "官_用", "複写", "灯油_購入", "応援", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL…
$ `as.factor(bidding_method_code)8003010_high` <chr> "補助_業務", "設備_改修", "構内", "ライセンス_購入", "統合", "情報_システム", "運用_保守", "ライセンス", "業務
$ `as.factor(bidding_method_code)8003010_low`  <chr> "京都", "ファイル", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "N…
$ `as.factor(bidding_method_code)8004020_high` <chr> "演習", "業務_1", "1_", "合同庁舎_清掃", "機_場", "変更", "用_部品", "追加", "西", "整備_単価", "賃貸借_業
$ `as.factor(bidding_method_code)8004020_low`  <chr> "等_工事", "1_4", "ファイル", "東京航空局", "東京航空局_千歳空港", "千歳空港_事務所", "課", "予定", "台", "…
$ `as.factor(bidding_method_code)8004025_high` <chr> "", "", "特別", "航空機", "ほか_1", "1", "特別_整備", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NUL
$ `as.factor(bidding_method_code)8004025_low`  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ `as.factor(bidding_method_code)8004030_high` <chr> "通訳", "リスク", "影響", "設計_業務", "調査_業務", "年度_地域", "促進_調査", "モデル", "規模", "日本", "生物"
$ `as.factor(bidding_method_code)8004030_low`  <chr> "ファイル", "机", "10", "ライセンス", "中央", "東京航空局_千歳空港", "千歳空港_事務所", "ソフトウェア", "中央_…
$ `as.factor(bidding_method_code)8011010_high` <chr> "", "会場", "用品_類", "コピー_用紙", "レイアウト_変更", "什器_購入", "設置_作業", "排_雪", "led", "レイアウ
$ `as.factor(bidding_method_code)8011010_low`  <chr> "近畿_経済", "東京航空局_千歳空港", "東京航空局", "千歳空港_事務所", "千歳空港", "事務所_令", "稚内", "年度_国…
$ `as.factor(bidding_method_code)8014020_high` <chr> "更新_作業", "機_等", "岐阜", "電話", "等_更新", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL
$ `as.factor(bidding_method_code)8014020_low`  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ `as.factor(bidding_method_code)8014025_high` <chr> "/", "内", "維持", "12", "2", "2_点", "個", "区域", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "…
$ `as.factor(bidding_method_code)8014025_low`  <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ log_price_std_high                           <chr> "型_法務局", "法務局_地図", "地図_作成", "", "m", "システム_更改", "更改", "施設_使用", "製造_据付", "収穫_
$ log_price_std_low                            <chr> "世代_活躍", "活躍_応援", "応援_プロジェクト", "応援", "年度_中高年", "中高年_世代", "重油_供給", "年度_単価",
$ ministry_codeA1_high                         <chr> "借入", "伝送", "外_5", "外_4", "3月", "放送", "3_点", "昇降_機", "ほか_3", "本館", "灯", "外_1", "外_3…
$ ministry_codeA1_low                          <chr> "実施", "運営", "年度", "情報", "", "", "令_和", "", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NU
$ ministry_codeB1_high                         <chr> "2_件", "点検_保守", "その他_工事", "昇降_機", "外_5", "ホームページ", "給水", "ゲート", "コンテンツ", "エレ…
$ ministry_codeB1_low                          <chr> "年度", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeC1_high                         <chr> "", "1_", "運用_保守", "等_作業", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL",…
$ ministry_codeC1_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeD1_high                         <chr> "地方裁判所", "支部_庁舎", "地方裁判所_庁舎", "簡易_裁判所", "地裁", "支部", "高等", "合同庁舎_等", "裁判所", …
$ ministry_codeD1_low                          <chr> "運営_業務", "", "7", "派遣", "補助", "開催", "和_7", "7_年度", "実施_業務", "導入", "(", "技術", "維持", ")
$ ministry_codeE1_high                         <chr> "リコー", "ファイル", "通訳", "等_管理", "機_更新", "各種", "機器_更新", "需給_契約", "電気_需給", "等_工事", 
$ ministry_codeE1_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeF1_high                         <chr> "国家", "報告_書", "級", "省", "府", "移転", "灯", "問題", "実現", "等_一式", "等_作成", "購入_等", "/", "年度…
$ ministry_codeF1_low                          <chr> "", "令_和", "", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "
$ ministry_codeG1_high                         <chr> "通訳", "演習", "管制", "衛星", "国土", "展示", "政府", "計測", "リスク", "府", "講習", "首都", "若年", "操作"
$ ministry_codeG1_low                          <chr> "交換", "6", "処理", "物", "5", "設置", "監視", "サービス", "自動車", "電話", "工事", "改修", "管理_業務", "…
$ ministry_codeH1_high                         <chr> "データベース", "器具", "実証", "支援_システム", "自動車_運行", "変更", "中央", "椅子", "審査", "運行_管理", "
$ ministry_codeH1_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeJ1_high                         <chr> "年度_経済", "業務_艇", "解消", "港湾_事務所", "港湾_業務", "就職", "府", "年度_原子力", "公園_事務所", "研究_…
$ ministry_codeJ1_low                          <chr> "試験", "移行", "ソフトウェア", "", "3", "機器_等", "情報_提供", "認証", "", "受付", "自動", "文書", "",…
$ ministry_codeJ2_high                         <chr> "その他_工事", "京都", "予定", "草刈", "交換_購入", "更新_工事", "", "測量", "", "整備_工事", "区域", "
$ ministry_codeJ2_low                          <chr> "実施", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeJ3_high                         <chr> "報告_書", "執務_室", "事業_者", "中小", "講習", "本局", "ソフトウェア_ライセンス", "会場", "等_調達", "政策",
$ ministry_codeJ3_low                          <chr> "実施", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeJ5_high                         <chr> "警察", "県_情報", "通信_部", "管区_警察", "電子_可", "管区", "情報_通信", "伝送", "新潟", "1_点", "鳥取", "…
$ ministry_codeJ5_low                          <chr> "運用_保守", "監視", "", "調達", "", "システム_運用", "運用", "運行", "運行_管理", "実施", "用品", "", "
$ ministry_codeJ6_high                         <chr> "報告_書", "モニタリング", "搬送", "ライセンス_購入", "設置_作業", "検知", "各種", "府", "者_用", "書類", "書_…
$ ministry_codeJ6_low                          <chr> "運営", "", "施設", "交換", "年度", "7_年度", "自動車", "", "令_和", "", "", "工事", "", "和_7", "
$ ministry_codeJ7_high                         <chr> "食品", "区分_3", "業務_公告", "災害_時", "区分", "本部", "3月", "調査_票", "状況_等", "表彰", "団体", "被害…
$ ministry_codeJ7_low                          <chr> "処理", "施設", "事務", "機器", "改修", "", "", "システム", "設置", "工事", "年度", "自動車", "NULL", "NUL
$ ministry_codeJ8_high                         <chr> "特殊", "広報_業務", "団体", "保護", "コンテンツ", "公共", "会_運営", "移転", "制度_等", "訓練", "事業_者", "…
$ ministry_codeJ8_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeJ9_high                         <chr> "事業_者", "等_賃貸借", "規制", "購入_等", "手続", "分析_業務", "", "翻訳", "什器_購入", "訓練", "労働_者", …
$ ministry_codeJ9_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeJA_high                         <chr> "家庭", "委託_一式", "応援", "一式_令", "事業_一式", "権利", "広報_業務", "全国", "世代", "調査_票", "局_業務"…
$ ministry_codeJA_low                          <chr> "施設", "", "国際", "機器", "処理", "", "", "", "設置", "監視", "自動車", "", "NULL", "NULL", "NULL"…
$ ministry_codeK1_high                         <chr> "0042", "-_0042", "0042_-", "08_-", "07_-", "0049", "-_0049", "0049_-", "07", "08", "調査_請負", "電波", "2_…
$ ministry_codeK1_low                          <chr> "管理_支援", "廃棄", "ライセンス", "廃棄_物", "保安", "支援_システム", "購入_単価", "一", "等_委託", "版", "賃…
$ ministry_codeK3_high                         <chr> "講習", "操作", "規模", "伝送", "搬送", "災害_時", "賃貸借_業務", "衛星", "全国", "消防", "記念", "接続", "
$ ministry_codeK3_low                          <chr> "事務", "6", "室", "2", "等_購入", "管理_業務", "設置", "電話", "3", "工事", "使用", "等", "NULL", "NULL", 
$ ministry_codeL1_high                         <chr> "在留_管理", "東京_出入国", "型_法務局", "法務局_地図", "出入国_在留", "鑑別", "少年_鑑別", "鑑別_所", "刑務所…
$ ministry_codeL1_low                          <chr> "開催", "安全", "受付", "プログラム", "強化", "会_運営", "データベース", "10", "什器_購入", "報告", "対象", "
$ ministry_codeL2_high                         <chr> "法務_合同庁舎", "検察庁", "地方_検察庁", "法務_総合", "支部_庁舎", "法務", "賃貸借_契約", "合同庁舎_等", "支…
$ ministry_codeL2_low                          <chr> "運用", "運営", "", "運用_保守", "作成", "", "利用", "支援", "支援_業務", "運営_業務", "分析", "", "",…
$ ministry_codeL4_high                         <chr> "トナーカートリッジ", "自動車", "トナーカートリッジ_等", "年度_自動車", "等_購入", "年間", "近畿", "供給_単価"…
$ ministry_codeL4_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeM1_high                         <chr> "通訳", "報告_書", "リスク", "オフィス", "", "統合_管理", "", "化学", "愛知", "", "新規", "協力", "委託_
$ ministry_codeM1_low                          <chr> "処理", "等_購入", "交換", "システム_運用", "施設", "補助", "管理_業務", "導入", "自動車", "管理_システム", "…
$ ministry_codeN1_high                         <chr> "監視_艇", "財務省", "合同_宿舎", "税関", "処分_等", "", "その他_工事", "年度_国有", "07", "年度_補正", "
$ ministry_codeN1_low                          <chr> "会", "ソフトウェア", "強化", "オンライン", "公告", "デジタル", "育成", "データベース", "人材", "相談", "体制"
$ ministry_codeN2_high                         <chr> "国税", "確定_申告", "刷", "刷_成", "税_確定", "税務署", "消費_税", "所得_税", "業務_区分", "申告_期", "国税局…
$ ministry_codeN2_low                          <chr> "運用_保守", "", "運行", "運行_管理", "自動車_運行", "中央", "導入", "安全", "研究", "実態_調査", "運営_等",…
$ ministry_codeO1_high                         <chr> "高等", "", "通訳", "科学", "研究所", "展示", "", "供給_一式", "学校", "事業_一式", "権利", "業務_一式", "
$ ministry_codeO1_low                          <chr> "交換", "3", "設置", "監視", "電話", "提供", "運営_業務", "機能", "検査", "健康", "サービス", "自動車", "健康…
$ ministry_codeO2_high                         <chr> "研究_事業", "", "", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL
$ ministry_codeO2_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeP1_high                         <chr> "労働_局", "監督_署", "公共_職業", "安定_所", "労働_基準", "東京_労働", "基準_監督", "年度_中高年", "世代_活躍…
$ ministry_codeP1_low                          <chr> "運行_管理", "自動車_運行", "等_請負", "資料_作成", "等_清掃", "年版", "運行", "システム_開発", "高度", "大型"…
$ ministry_codeP2_high                         <chr> "", "維持_管理", "", "自動車_運行", "維持", "運行_管理", "運行", "管理_業務", "委員", "NULL", "NULL", "NUL
$ ministry_codeP2_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeQ1_high                         <chr> "農林", "農林_水産", "農政", "農政_局", "検疫_所", "検疫", "動物", "うち", "水産", "年度_国有", "農地", "リス…
$ ministry_codeQ1_low                          <chr> "開催", "カウンター", "オープン", "電話_交換", "", "受付", "等_調達", "購入_等", "運転", "沖縄", "募集", "
$ ministry_codeQ2_high                         <chr> "収穫_調査", "収穫", "署_収穫", "地区_建設", "砕石_購入", "森林_管理", "方式_見積", "電子_調達", "管理_署", "…
$ ministry_codeQ2_low                          <chr> "外国", "研修", "電話", "国際", "研究", "情報_システム", "開発", "", "導入", "広報", "作成_業務", "派遣", "
$ ministry_codeQ3_high                         <chr> "取締_船用", "水産庁_漁業", "調査_取締", "船用_燃料", "漁業_調査", "水産庁", "丸", "漁業", "船用", "油_購入", 
$ ministry_codeQ3_low                          <chr> "実施", "物", "自動車", "事務", "処理", "室", "作成", "6", "1", "研修", "外国", "5", "派遣", "賃貸借", "提…
$ ministry_codeR1_high                         <chr> "近畿_経済", "産業_局", "年度_経済", "産業_保安", "経済_産業", "年度_エネルギー", "年度_補正", "産業_省", "
$ ministry_codeR1_low                          <chr> "研究_業務", "3", "クラ", "ウド", "クラ_ウド", "保守_等", "データベース", "解析", "5", "期", "機器_等", "植", 
$ ministry_codeR3_high                         <chr> "財", "知_財", "知", "通訳", "年度_中小", "予定", "簡易_型", "報告_書", "伝送", "中小_企業", ",", "経済_産業",
$ ministry_codeR3_low                          <chr> "交換", "施設", "工事", "事務", "自動車", "設置", "設計", "監視", "対応", "室", "人材", "会", "研修", "保守_業…
$ ministry_codeS1_high                         <chr> "工事_外", "東京航空局", "地方_整備", "東京航空局_千歳空港", "千歳空港_事務所", "荒川", "空港_事務所", "砂防_
$ ministry_codeS1_low                          <chr> "電気_供給", "セミナー", "什器_等", "什器", "通知", "周知", "オープン", "自動車_運行", "1_回", "作業_等", "等…
$ ministry_codeS2_high                         <chr> "飛行", "無人", "報告_書", "2_", "4_", "3_", "", "点_購入", "", "解析", "画像", "派遣_業務", "
$ ministry_codeS2_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeS3_high                         <chr> "通訳", "年度_地域", "等_作成", "全国", "協力", "会場", "登録", "等_管理", "経営", "分析_等", "業", "10", "印…
$ ministry_codeS3_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"…
$ ministry_codeS4_high                         <chr> "地方_気象台", "気象台", "管区", "取付", "", "気象", "観測_装置", "伝送", "衛星", "稚内", "更新_工事", "回_
$ ministry_codeS4_low                          <chr> "8", "和_8", "物", "8_年度", "会", "検査", "システム_運用", "年度", "運行_管理", "和", "令_和", "令", "9",
$ ministry_codeS5_high                         <chr> "型_巡視", "巡視_艇", "買_入", "点_買", "海上_保安", "5_-", "四_管区", "本部__", "軽油_免税", "入_単価", "買…
$ ministry_codeS5_low                          <chr> "購入", "等_購入", "", "システム_運用", "事務_用", "運行", "運行_管理", "対応", "自動車_運行", "実施_業務", …
$ ministry_codeT1_high                         <chr> "国立_公園", "繰越", "公園", "業務_[", "方式_]", "[_総合", "捕獲", "鳥獣", "化学_物質", "日光", "見積", "生物"…
$ ministry_codeT1_low                          <chr> "等_購入", "プログラム", "事務_用", "乗用_自動車", "庁舎", "3", "印刷", "認証", "ライセンス", "機器_等", "業務
$ ministry_codeT2_high                         <chr> "委託_費", "年度_原子力", "体", "原子力", "規制", "モニタリング", "リスク", "災害_時", "性能", "11_年度", "業…
$ ministry_codeT2_low                          <chr> "自動車", "改修", "交換", "事務", "作成", "利用", "", "システム_運用", "", "", "情報_システム", "機能", …
$ ministry_codeU1_high                         <chr> "住宅_防音", "陸_装", "装_研", "局_7", "防衛_局", "支援_役務", "防音_事業", "(_7", "防衛", "防音", "飛行場",…
$ ministry_codeU1_low                          <chr> "", "実施_業務", "自動車_運行", "", "保守_業務", "実態_調査", "8_年度", "運用_保守", "和_8", "業務_請負", …
$ ministry_codeV1_high                         <chr> "宮城", "財務", "システム_更改", "福島", "東北", "ホームページ", "年度_産業", "等_作成", "書類", "会_運営", "
$ ministry_codeV1_low                          <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"
$ ministry_codeW1_high                         <chr> "年度_デジタル", "2_号館", "借入", "合同庁舎_2", "政府", "執務_室", "出入国_在留", "納付", "支援_役務", "線"
$ ministry_codeW1_low                          <chr> "外国", "解析", "影響", "設計_業務", "日本", "6", "物", "交換", "処理", "検討", "調査_業務", "5", "印刷", "…

まず、何も調整していないモデルでは、落札価格と正の関連が強い単語として、

設計_開発
システム_更改
更改
整備_保育
管内_電気
森林_環境
保育_間伐
間伐
環境_保全

などが上位に現れました。

反対に、負の関連が強い単語としては、

重油_供給
3_四半期
灯油_供給
4_四半期
四半期
白_灯油
供給_単価
年度_単価

などが現れています。

この時点では、

システム開発や更改、森林整備のような案件は高額になりやすく、燃料供給や単価契約は比較的低額になりやすいのかな

という、それなりにもっともらしいストーリーを作ることができます。

しかし、今回の記事でやりたいのは、ここでストーリーを作って終わることではありません。

府省をモデルに追加すると、log_price_std の上位には、

整備_保育
森林_環境
保育_間伐
間伐
保全_整備
間伐_活用
保育

などが引き続き強く現れる一方、負の方向には、

方式_見積
見積
カウンター_方式
重油_供給
年度_単価
船用_燃料
水産庁_漁業

などが現れました。

すでに、何も調整していないモデルとはかなり順位が変わっています。

ただし、ここでもう一つ面白いものが見えます。

コード名を知らなくても、どの組織なのかだいたい分かる

府省コードについて推定された単語を見てみると、かなり露骨です。

例えば ministry_codeD1 では、

地方裁判所
地方裁判所_庁舎
地裁
簡易_裁判所
高等
裁判所

といった単語が上位に現れます。

ministry_codeJ5 では、

警察
管区_警察
通信_部
情報_通信
通信_施設

が出てきます。

ministry_codeN2 では、

確定_申告
国税
税務署
国税局
所得_税
申告_期

です。

さらに、

ministry_codeQ1
→ 農林_水産、農政_局、検疫、農地、動物、国営

ministry_codeQ2
→ 森林_環境、保育_間伐、間伐_活用、森林_整備

ministry_codeQ3
→ 水産庁_漁業、漁業_調査、水産庁、船用_燃料

となっています。

府省コードの正式な対応表を見ていなくても、

だいたい何をやっている組織なのか分かりませんか?

これは今回かなり重要な結果です。

モデルには、

N2 は税務を担当する組織ですよ

とか、

Q2 は森林関係ですよ

といった意味情報を教えていません。

それでも、案件名に含まれる単語を見るだけで、その組織の特徴がかなり明確に出ています。

つまり、テキストの中には、

$$
\text{案件そのものの内容}
$$

だけでなく、

$$
\text{どの府省がその案件を発注したのか}
$$

という情報まで大量に含まれていることになります。

先ほどのECサイトの例でいう、

東京
新宿
下北沢

から、

prefecture = "東京都"

が透けて見える状態とほぼ同じです。

入札方式もテキストに埋め込まれている

さらに府省コードと入札方式コードの両方を入れたモデルを見ると、入札方式についても同じことが起きています。

例えば、ある入札方式では負の方向に、

方式_見積
カウンター_方式
オープン_カウンター
オープン
見積
カウンター

といった単語が並んでいます。

別の入札方式でも、

見積
方式_見積
カウンター
カウンター_方式
オープン_カウンター

が負の方向の上位に現れています。

つまり、

案件名のテキストを読めば、入札方式についてもかなりの情報が分かってしまう

ということです。

ここまでの結果をまとめると、

$$
\text{府省・入札方式}
\rightarrow
\text{落札価格}
$$

という関係があるだけでなく、

$$
\text{府省・入札方式}
\rightarrow
\text{案件名に使われる単語}
$$

という関係もかなり強そうです。

したがって、単純に

$$
\text{単語}
\leftrightarrow
\text{落札価格}
$$

だけを見ていると、実際には既存の構造化変数である府省や入札方式を、テキストからもう一度発見しているだけかもしれません。

まさに、先ほどの

東京都の商品はよく売れる

東京都の商品には「新宿」と書かれる

「新宿」は売れる商品の重要な特徴です!

という問題です。

では、府省と入札方式の両方をモデルに入れたあと、落札価格そのものと関連する単語はどう変わるのでしょうか。

ここから、3つのモデルの log_price_std の係数を直接比較していきます。

構造化情報を追加すると、落札価格と関連する単語はどう変わるのか

ここからは、3つのモデルで log_price_std の係数を直接比較していきます。

今回推定したモデルは、次の3つでした。

m_dmr_naive
  落札価格のみ

m_dmr_ministry
  落札価格 + 府省コード

m_dmr_ministry_bidding
  落札価格 + 府省コード + 入札方式コード

つまり、

既存の構造化情報を追加するにつれて、「落札価格と関連する単語」がどう変わるのか

を見ていきます。

まずは、何も調整していないモデルです。

正の係数が大きい単語には、

設計_開発
システム_更改
更改
整備_保育
管内_電気
森林_環境
保育_間伐
間伐
環境_保全

などが現れました。

一方、負の係数が大きい単語には、

重油_供給
3_四半期
灯油_供給
4_四半期
四半期
白_灯油
供給_単価
年度_単価

などが並びます。

ここだけを見ると、

システム開発や更改、森林整備に関する案件は高額になりやすく、燃料供給や単価契約は低額になりやすい

という、それなりに自然な説明をしたくなります。

しかし、府省コードを追加すると結果はかなり変わります。

正の方向では、

整備_保育
森林_環境
保育_間伐
間伐
保全_整備
間伐_活用
保育
活用_型
事業_保育
環境_保全

と、森林・間伐関係の単語がさらに強く現れます。

反対に、負の方向では、

方式_見積
見積
カウンター_方式
重油_供給
年度_単価
船用_燃料
水産庁_漁業
調査_取締
漁業_調査
オープン_カウンター

といった単語が上位に現れました。

ここで重要なのは、

調整変数を追加したからといって、すべての係数が単純に0へ縮んでいるわけではない

ということです。

むしろ、府省コードを追加したことで、森林関係の単語は以前より目立つようになっています。

これは、

調整 = 係数を小さくする作業

ではないことを示しています。

既存の構造化情報によって説明されていた部分を分けることで、逆にそれまで隠れていた関係が強く見えることもあります。

「見積」「カウンター方式」は本当に価格の特徴なのか

府省コードだけを調整したモデルでは、

方式_見積
見積
カウンター_方式
オープン_カウンター

といった単語が、低い落札価格と強く関連する単語として上位に現れました。

ここだけを見ると、

見積やオープンカウンターに関する案件は、そもそも価格が低いのでは?

と考えたくなります。

しかし、前節で見たように、これらの単語は特定の入札方式とも強く関連しています。

つまり、

$$
\text{入札方式}
\rightarrow
\text{「見積」「カウンター方式」などの単語}
$$

であると同時に、

$$
\text{入札方式}
\rightarrow
\text{落札価格}
$$

でもあります。

したがって、

$$
\text{「カウンター方式」}
\leftrightarrow
\text{落札価格}
$$

という見かけ上の関係の一部は、実際には入札方式そのものを反映している可能性があります。

これは、先ほどのECサイトでいう、

新宿

が購入と関連しているように見えても、実際には

prefecture = "東京都"

を反映しているだけかもしれない、という問題と同じです。

そこで最後に、府省コードだけでなく、入札方式コードもモデルに追加します。

府省と入札方式の両方を調整すると

府省コードと入札方式コードの両方を追加したモデルでは、log_price_std の上位はさらに大きく変わりました。

正の方向では、

型_法務局
法務局_地図
地図_作成
級
m
システム_更改
更改
施設_使用
製造_据付
収穫_...

などが現れます。

一方、負の方向では、

世代_活躍
活躍_応援
応援_プロジェクト
応援
年度_中高年
中高年_世代
重油_供給
年度_単価
...

などが上位になりました。

府省だけを調整したときに上位だった、

方式_見積
見積
カウンター_方式
オープン_カウンター

といった単語は、最上位から姿を消しています。

これはかなり分かりやすい結果です。

これらの単語は、単純に「価格が低い案件に特徴的な単語」だったというより、

特定の入札方式をテキスト上で代わりに表現していた

可能性が高いと考えられます。

つまり、入札方式という情報をすでに構造化データとして持っているのであれば、

「カウンター方式」という単語が重要です!

とプロダクト側に提案しても、あまり意味がありません。

それは、すでに持っている情報をテキストから再発見しているだけだからです。

一方で、残る単語もある

すべての単語が消えるわけではありません。

例えば、

システム_更改
更改

は、府省と入札方式の両方を調整したあとでも、落札価格と正の方向で強く関連する単語として残っています。

また、

重油_供給
年度_単価

なども、負の方向で引き続き上位に現れています。

ここが今回のプロダクト開発の話につながります。

既存の構造化情報を追加したあとでも残る単語は、

今のデータベースにはまだ明示的に持っていないが、目的変数と関連している情報

の候補になり得ます。

もちろん、

「システム更改」という単語があるから落札価格が高くなる

という因果関係が証明されたわけではありません。

案件規模、契約期間、設備量、技術的難易度など、まだ観測していない要因はいくらでもあります。

したがって、ここで得られるものは、

プロダクトに追加すべき項目を自動的に決定する答え

ではありません。

そうではなく、

既存の構造化情報では説明されていない、次に調べる価値がありそうなテキスト上の特徴

です。

今回の結果で面白いのは、構造化情報を増やすにつれて、

  • 消える単語
  • 強くなる単語
  • ほとんど残り続ける単語
  • 新しく上位に現れる単語

がそれぞれ出てきたことです。

つまり、調整とは単に「怪しい単語を消す」ことではありません。

どの比較をしたいのかを、構造化変数によって明確にする作業だと考えたほうがよさそうです。

次に、この結果を最初のECサイトのプロダクトデザインの話へ戻して考えてみます。

DMRの結果をどうプロダクト開発につなげるか

ここまで、政府調達データを使って、

既存の構造化情報を考慮したあとにも、目的変数と関連するテキスト上の特徴を探す

という問題を見てきました。

では、このような結果を実際のプロダクト開発ではどう使えばよいのでしょうか。

重要なのは、DMRの係数を見て、

この単語が上位だから、この項目を明日から検索条件に追加しよう!

と自動的に意思決定することではありません。

今回のECサイトの例に戻ると、私たちが欲しいのは、

既存のプロダクトではまだ構造化されていないが、ユーザー行動と関連していそうな情報の候補

です。

例えば、都道府県を調整したあとにも、

世界的ブランド
限定
ハンドメイド

といった単語群が購入と関連して残ったとします。

この結果から、

「ブランドの国際的な知名度」
「限定商品かどうか」
「ハンドメイドかどうか」

といった、より抽象的な商品属性が存在するのではないかと考えることができます。

ただし、実際のDMRの出力は、このように人間にとって都合よく整理された概念名ではありません。

大量の単語やn-gramと、それぞれの係数です。

そこで、ここではLLMがかなり便利です。

例えば、DMRで得られた係数上位の単語をLLMに渡して、

これらの単語に共通する意味的なグループを考えてください

と依頼したり、

これらの単語が示している可能性のある潜在的な商品属性をいくつか仮説として提案してください

と依頼したりできます。

つまり、

$$
\text{DMR}
\rightarrow
\text{係数の大きい単語群}
\rightarrow
\text{LLMによる意味的な整理・仮説生成}
$$

という使い方です。

例えば、

海外展開
世界
老舗
欧州
グローバル

のような単語がまとまって残っていれば、LLMは、

「国際的なブランド認知度」という潜在的な属性を表している可能性があります

と整理してくれるかもしれません。

あるいは、

手作り
職人
工房
一点物

のような単語群から、

「ハンドメイド性」「クラフト性」

という候補を作ることもできるでしょう。

こうして作られた仮説は、そのままプロダクト仕様になるわけではありません。

むしろ、ここから先がドメインエキスパートとの議論です。

例えば、

本当にユーザーは「国際的なブランド」であることを重視しているのか?

それとも、別の要因を代理しているだけなのか?

その情報を検索条件として出すと、ユーザー体験は改善するのか?

そもそも、プロダクトとしてその属性を定義・運用できるのか?

といったことを、プロダクトマネージャー、営業、マーケティング、ドメインエキスパートなどと議論できます。

この意味で、DMRの結果は、

議論を始めるためのデータに基づいた材料

として使うことができます。

「LLMに聞けばいい」とはやはり違う

ここで改めて、

だったら最初からLLMに「ユーザーが重視している特徴を教えて」と聞けばいいのでは?

という疑問が出てきます。

しかし、これは同じことではありません。

例えばLLMに、

このECサイトでユーザーが重視していそうな商品属性を考えてください

と聞けば、

ブランド
価格
色
原産国
レビュー
配送速度
サステナビリティ

など、もっともらしい候補はいくらでも出てきます。

人間の専門家に聞いても同じです。

経験や知識から、

たぶんブランドが重要でしょう

という仮説を作ることはできます。

そして、その後で実データを探して、

ブランド商品は購入率が高かったので、やはり重要でした

と説明することもできます。

それ自体が悪いわけではありません。

仮説生成としては非常に有用です。

しかし、今回の問題では、

既存の構造化情報を考慮したあとにも、目的変数と関連する特徴は何か

というルールを先に定義しています。

そして、そのルールをデータ全体に対して同じように適用しています。

つまり、

LLM / 人間
→ まず仮説を考える
→ データから確認する

という流れと、

DMR
→ 定義した統計的基準で候補を抽出する
→ LLM / 人間が意味を考える

という流れは、かなり違います。

後者では、少なくとも、

自分が思いついた馴染みのある現象だけを選んでしまう

という自由度をある程度減らすことができます

もちろん、DMR側にも前処理、モデル、ペナルティ、調整変数の選択など多くの分析上の判断があります。

したがって、完全に客観的な自動発見装置ではありません。

それでも、

「何を探しているのか」

を事前にかなり明確に定義できることには意味があります。

統計モデルとLLMは競争相手ではない

今回の話は、

古い統計モデルのほうがLLMより優れている

という話ではありません。

むしろ、役割を分けるとかなり相性がよいと思います。

例えば、

$$
\text{DMRで候補を探索}
$$

$$
\downarrow
$$

$$
\text{LLMで意味的なグルーピング・仮説生成}
$$

$$
\downarrow
$$

$$
\text{ドメインエキスパートと議論}
$$

$$
\downarrow
$$

$$
\text{ユーザー調査やA/Bテストで検証}
$$

$$
\downarrow
$$

$$
\text{有用ならプロダクトの構造化項目として実装}
$$

という流れを考えることができます。

そして、一度その情報が本当に重要だと分かれば、わざわざ毎回LLMに自由記述を読ませる必要もありません。

例えば、

international_brand = TRUE

という構造化項目として持っておけば、ユーザーも検索できますし、推薦システムも分析もLLMエージェントも、その情報をそのまま利用できます。

LLMがテキストから情報を推論できるようになったことと、すべての情報をテキストのまま持っておくべきことは同じではありません。

そして、何を構造化する価値があるのかを考えるために、DMRのような計量経済学由来のテキスト解析手法は、LLM時代でも十分に使い道があると思います。

おわりに

いかがでしたか?

今回は、計量経済学由来のテキスト解析手法であるDMRを使って、

既存の構造化情報ではまだ表現されていないが、ユーザー行動と関連している情報を探し、プロダクトデザインの候補を作る

という使い方を考えてみました。

記事では実際のECサイトのデータを公開するわけにはいかないので、政府調達のオープンデータを使い、

  • 落札価格だけを見るモデル
  • 府省コードを調整するモデル
  • 府省コードと入札方式コードを調整するモデル

を比較しました。

結果として、構造化情報を追加すると、落札価格との関連が強い単語の順位はかなり変わりました。

特に面白かったのは、府省だけを調整したモデルでは低価格側に現れていた「見積」「カウンター方式」のような単語が、入札方式そのものを調整すると上位から消えたことです。

これは今回の記事で考えたかった問題をかなり素直に表しています。

ECサイトですでに「都道府県」という項目を持っているのに、

「東京」「新宿」「下北沢」という単語が購入と関連しています!

と言われても、あまり嬉しくありません。

私たちが知りたいのは、そのような既知の構造を取り除いたその先です。

例えば、都道府県を調整しても「世界」「海外」「老舗」のような単語群が購入と関連して残るのであれば、

もしかして「国際的なブランド認知度」のような属性を構造化する価値があるのでは?

という議論を始めることができます。

もちろん、DMRの係数は因果効果ではありません。

係数が大きいからといって、その単語に対応する検索条件を追加すれば売上が増える、といった話でもありません。

しかし、

次に何を調べるべきか

をデータから探索する材料にはなります。

さらに現在であれば、その大量の係数を人間が一つずつ眺める必要もありません。

DMRで候補を抽出し、LLMに意味的なグルーピングや仮説生成をさせ、その結果をドメインエキスパートと議論することもできます。

ただし、これは、

「LLMさん、ユーザーが重視している特徴を教えてください」

と聞くこととは違います。

後者では、何を根拠に、どの情報を考慮して、どの候補の中から特徴が選ばれたのかが曖昧です。

今回の方法では少なくとも、

既存の構造化情報を調整したうえで、目的変数と関連するテキスト上の特徴を探す

という探索ルールを先に定義しています。

その上でLLMを使うのであれば、LLMは統計分析の代替というより、統計分析によって作られた探索空間を解釈する道具になります。

LLMが非常に強力になったからといって、昔からある統計モデルや計量経済学的な発想が突然不要になるわけではありません。

むしろ、

$$
\text{統計モデルによる探索}
\rightarrow
\text{LLMによる意味解釈}
\rightarrow
\text{人間によるドメイン判断}
\rightarrow
\text{実験による検証}
\rightarrow
\text{プロダクトへの実装}
$$

のように、それぞれ得意な仕事を担当させればよいと思います。

最新のLLMがある時代に、あえて昔からあるテキスト解析を使う。

しかし、それは懐古趣味ではありません。

問いがきちんと定義できるのであれば、その問いに特化した道具を使えばいい。

そして、その結果をLLMにも人間にも読ませて、次のプロダクトを考えればいい。

今回の記事が、そんな分析とプロダクト開発のつなぎ方の一例になれば幸いです。

最後に、私たちと一緒に働きたい方はぜひ下記のリンクもご確認ください:

参考文献

Taddy, Matt. "One-step estimator paths for concave regularization." Journal of Computational and Graphical Statistics 26.3 (2017): 525-536.

Taddy, Matt. "Distributed multinomial regression." The Annals of Applied Statistics 9.3 (2015): 1394-1414.

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?