13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

先日の第3回GenAIアナリティクス@東京でお話ししたLTが割と評判良かったようなので、改めてこちらの記事でもう少し詳しく紹介したいと思います。


LLM(生成AI)の時代が来てしまいましたね。
ロートルはついていくのが大変です。

さて、われらがR言語にもLLMと連携する仕組みがいろいろ出てきました。

すでにRStudioとGitHub Copilotを連携させてvibe codingしたり、RStudioからGemini CLIを使ったりできるようになっています。

また、RStudioの後継IDEであるPositronではさらに深くLLMと連携できるという話もあります。

今回は、LLM連携を容易にする各種パッケージの紹介と実際の利用例を紹介したいと思います。
まず、代表的なLLM連携パッケージをいくつか紹介し、そのあといくつかのパッケージについて具体的な使い方を紹介します。

代表的なLLM連携パッケージの紹介

ellmer

LLMのAPIに接続するための基本パッケージ。Posit社のHadley Wickhamが中心となって開発されています。この後紹介する各種パッケージもellmerに依存するものが多いです。

tidyllm

こちらもLLMのAPIに接続するためのパッケージ。ellmerとは設計思想が異なります。

rollama

ローカルLLMツールOllamaに接続するパッケージ。

chattr

RStudioのViewer上でLLMとチャットできるパッケージ。主に探索的データ分析(EDA)を支援するために設計された、対話型のチャットインターフェースです 。  

gander

「Copilotライク」な、いわゆるVibe Codingを提供するコード生成アシスタント。ユーザーのコードとR環境内のオブジェクトに関するコンテキストを自動的にプロンプトに注入することで、高精度なコード生成を実現します。これにより、ユーザーは抽象的な指示を出すだけで、ganderが具体的なコードを生成してくれます。

shinychat

shinyアプリ上でLLMとチャットできるパッケージ。

valentine

R言語に関する詩を書くパッケージ。おちゃめですね。

vitals

各種LLMの比較評価を行うパッケージ。Pythonのフレームワークである「Inspect」の移植版。

mall

LLMを用いた自然言語処理のためのパッケージ。テキストの感情分析、要約、分類、情報抽出、翻訳、などが手軽に行えます。

emend

テキストデータのクリーニング。カテゴリ変数や自由記述の住所など、一貫性のないテキストデータを扱う際に、LLMの能力を利用してタイポの修正、略語の展開、不統一なエントリの標準値へのマッピングなどを行います。

tidyprompt

R上でプロンプトを組み立てるパッケージ。
これは使いこなすと便利そう。

具体的な利用例

ellmer

ellemerパッケージは各種LLMをRから利用するための基本パッケージです。
Claude、ChatGPT、DeepSeek、Gemini、Ollama、perplexityなどなど、代表的なLLMが利用できます。
今回はGeminiを利用する例を紹介します。

まず最初にAPI KEYを設定する必要があります。
GeminiのAPI KEYはこちらで確認できます。
API KEY は .Renviron に書き込んでおけば環境変数として使ってくれます。
.Renviron に書き込むには usethis パッケージのedit_r_environ()関数を利用するのが楽です。

usethis::edit_r_environ()

これでRStudioのエディター上に.Renvironが開きますので以下のようにAPI KEYを記述し保存します。
Rを再起動すると環境変数として反映されます。

.Renviron
GEMINI_API_KEY="**********"

再起動したら早速ellmerパッケージを呼び出しましょう。

library(ellmer)

まず、利用するモデルを指定してチャット・オブジェクトを作ります。
chat_google_gemini()関数で、今回は2.5 Flashをモデルとして指定します。
システムプロンプトは指定してもしなくても大丈夫です。

chat <- chat_google_gemini(
  model = "gemini-2.5-flash",
  system_prompt = "あなたはアメリカ人のコメディアンです。"
  )

簡単なやりとりをしてみましょう。

chat$chat("小粋なアメリカンジョークを1つ紹介してください")

以下が出力です。もちろん、毎回出力内容は変わります。


ヘイ、みんな!調子どう?

さて、俺はエクササイズが大好きなんだ。特に、俺のお気に入りのエクササイズがあるんだよね。

それはね、ランジとクランチを組み合わせたやつなんだ。

そう、その名も… 「ランチ」!

HAHAHA! 最高だろ? これなら毎日できるってもんさ!

(訳注:Lunge(ランジ)とCrunch(クランチ)というエクササイズ名を、両者を合わせたかのように「Lunch(ランチ)」と発音。発音の類似性を利用したダジャレジョークです。)


……面白い……でしょうか?

画像を読ませることもできます。
ネット上にある画像はcontent_image_url()関数でURLを指定、ローカルにある画像はcontent_image_file()でパスを指定、Rで描いたグラフはcontent_image_plot()で指定できます。
ただ、GeminiはURLでの指定に対応していません。

グラフの解釈をさせてみましょう。
まず、グラフを描きます。

library(ggplot2)
data(penguins)
penguins |>
  na.omit() |>
  ggplot(aes(x = bill_len, y = bill_dep)) +
  geom_point(aes(color = species, shape = species), size = 2) +
  geom_smooth(method = "lm", se = FALSE, aes(color = species)) +
  scale_color_manual(values = c("darkorange", "darkorchid", "cyan4")) +
  labs(title = "Palmer Archipelago Penguin Data")

image.png

このグラフをGeminiに渡して解説してもらいます。

chat$chat(content_image_plot(), "このグラフから読み取れることを解説してください")

以下が出力例です。


このグラフは、パーマー諸島(Palmer
Archipelago)に生息する3種類のペンギン(アデリーペンギン、ヒゲペンギン、ジェンツーペンギン)のくちばしの長さ(bill_len、横軸)と深さ(bill_dep、縦軸)の関係
を示した散布図です。

各点は個々のペンギンの測定値を表し、色と形状(丸、三角、四角)で種類が区別されています。各種類のペンギンのデータには、それぞれ対応する色の回帰直線(傾向線)が引かれており、くちばしの長さと深さの間の関係性を示しています。

このグラフから読み取れる主な点は以下の通りです。

  1. 種の明確な分離:

    • ジェンツーペンギン(緑の四角): くちばしの長さが最も長く(約40mm~60mm)、深さが最も浅い(約14.5mm~17.5mm)という特徴を持ち、他の2種から最も明確に分離されたグループを形成しています。
    • アデリーペンギン(オレンジの丸): くちばしの長さが最も短く(約33mm~45mm)、深さが最も深い(約17.0mm~21.5mm)傾向にあります。グラフの左上に集中しています。
    • ヒゲペンギン(紫の三角):
      くちばしの長さと深さにおいて、アデリーペンギンとジェンツーペンギンの中間的な位置に分布しています。特にくちばしの深さではアデリーペンギンと一部重なる部分も見られますが、全体的にはアデリーペンギンよりも長く、ジェンツーペンギンよりも短い傾向があります。
  2. くちばしの長さと深さの相関:

    • 3種すべてにおいて、くちばしの長さが増すにつれて深さも増すという正の相関が見られます(各色の傾向線が右肩上がり)。これは、くちばしが長いペンギンほど、くちばしも厚みがある(または高さがある)傾向にあることを示唆しています。
    • 特にジェンツーペンギンとヒゲペンギンでは、この正の相関が比較的強く、データ点が傾向線に沿って密に分布しています。アデリーペンギンも正の相関を示していますが、他の2種に比べてデータのばらつきがやや大きいように見えます。

このグラフは、くちばしの長さと深さという2つの形態的特徴が、これら3種類のペンギンを効果的に識別するための重要な指標となることを明確に示しています。特にジェンツーペンギンは、これらの特徴によって他の2種と容易に区別できることがわかります。


なかなかもっともらしい解説が出てきましたね!

さて、Rと言えばデータフレームです。
LLMの出力をデータフレームで得られたら便利ですよね。
parallel_chat_structured()関数を使えばできます!

それでは俳句の季語をLLMに抜き出してもらいましょう。

chat_haiku <- chat_google_gemini(
  model = "gemini-2.5-flash",
  system_prompt = "あなたは日本の俳句の研究者です。特に季語を研究のテーマとしています。"
)

haikus <- list(
  "古池や蛙飛びこむ水の音", "夏草や兵どもが夢の跡", "閑さや岩にしみ入る蝉の声",
  "五月雨をあつめて早し最上川", "秋深き隣は何をする人ぞ",
  "旅に病んで夢は枯野をかけ廻る", "我と来て遊べや親のない雀",
  "すずめの子そこのけそこのけお馬が通る", "分け入つても分け入つても青い山",
  "すべつてころんで山がひつそり", "柿くへば鐘が鳴るなり法隆寺",
  "咳をしても一人", "寒雷やびりりびりりと真夜の玻璃"
)

# 結果はデータフレームで返ってくる
res_df <- parallel_chat_structured(
  chat = chat_haiku,
  prompts = haikus,
  type = type_object(
    haiku = type_string("俳句の原文。"),
    haiku_author = type_string("俳句の作者名。"),
    seasonal_word = type_string("季語。季語がない場合は「無季」とする。")
  )
)

データはlistかvectorで渡します。
出力されるデータフレームの型を指定して、その中で抜き出したい情報を記述するのが肝です。

出力されたデータフレームはこのようになっています。

                                  haiku haiku_author seasonal_word
1                古池や蛙飛びこむ水の音     松尾芭蕉            蛙
2                  夏草や兵どもが夢の跡     松尾芭蕉          夏草
3              閑さや岩にしみ入る蝉の声     松尾芭蕉        蝉の声
4            五月雨をあつめて早し最上川     松尾芭蕉        五月雨
5                秋深き隣は何をする人ぞ     松尾芭蕉        秋深き
6          旅に病んで夢は枯野をかけ廻る     松尾芭蕉          枯野
7              我と来て遊べや親のない雀     小林一茶            雀
8  すずめの子そこのけそこのけお馬が通る     小林一茶    すずめの子
9        分け入つても分け入つても青い山   種田山頭火        青い山
10         すべつてころんで山がひつそり         不明          無季
11           柿くへば鐘が鳴るなり法隆寺     正岡子規            柿
12                       咳をしても一人     尾崎放哉            咳
13       寒雷やびりりびりりと真夜の玻璃   河東碧梧桐          寒雷

ちなみに「すべつてころんで山がひつそり」は山頭火、「寒雷やびりりびりりと真夜の玻璃」は加藤楸邨が正解です。

現代的な俳句でも試してみましょう。現代俳句データベースから何句か持ってきました。

haikus <- list(
  # 春
  "伸び過ぎのアスパラガスほどな粗忽", # アスパラガス
  "きのふ来た人を見送る風車", # 風車
  "けむり吐くような口なり桜鯛", # 桜鯛
  
  # 夏
  "あくまでも故郷めざす蝸牛", # 蝸牛
  "三社祭電気ブランに感電す", # 三社祭
  "海に手をついてサーファー立ち上がる", # サーフィン

  # 秋
  "どの径を行くも晩年葛の花", # 葛の花
  "包丁の力を決めし鮭一匹", # 鮭
  "台風一過あすからはダイエット", # 台風

  # 冬
  "選り好みしても鮟鱇同じ貌", # 鮟鱇
  "牡蠣殻を重ねて人を好きになる", # 牡蠣
  "ストーブの列車が醸す津軽弁", # ストーブ

  # 新年
  "お年玉貰う子素早く風になり", # 年玉
  "金色は母の温もり福寿草", # 福寿草
  "作り手を待ちて一面仏の座" # 仏の座
)

res_df <- parallel_chat_structured(
  chat = chat_haiku,
  prompts = haikus,
  type = type_object(
    haiku = type_string(),
    seasonal_word = type_string("俳句の季語。季語がない場合は「無季」とする。"),
    season = type_string("俳句の季語が表す季節。春、夏、秋、冬、新年、無季のいずれか。"),
  )
)

結果です。

                                haiku seasonal_word season
1    伸び過ぎのアスパラガスほどな粗忽  アスパラガス     夏
2            きのふ来た人を見送る風車          風車     夏
3          けむり吐くような口なり桜鯛          桜鯛     春
4            あくまでも故郷めざす蝸牛          蝸牛     夏
5            三社祭電気ブランに感電す        三社祭     夏
6  海に手をついてサーファー立ち上がる    サーファー     夏
7            どの径を行くも晩年葛の花        葛の花     夏
8              包丁の力を決めし鮭一匹            鮭     秋
9        台風一過あすからはダイエット      台風一過     夏
10           選り好みしても鮟鱇同じ貌          鮟鱇     冬
11       牡蠣殻を重ねて人を好きになる        牡蠣殻     冬
12         ストーブの列車が醸す津軽弁      ストーブ     冬
13         お年玉貰う子素早く風になり        お年玉   新年
14             金色は母の温もり福寿草        福寿草   新年
15           作り手を待ちて一面仏の座        仏の座     春

季語は完璧といっていいでしょうが、それに対応した季節を推定するのがなかなか難しいようです。
もっと賢いLLMを使えばイケるかな?

mall

mallパッケージはLLMを使った自然言語処理のためのパッケージです。
テキストの感情分析、要約、分類、情報抽出、翻訳、などが手軽に行えます。

なお、2025年7月現在リモートLLMを使うには開発版が必要ですので以下のように開発版をインストールしておきます。

pak::pak("mlverse/mall/r")

まず、実験のためのダミーデータを生成しちゃいましょう。
こちらを参考にして、ダミーデータ生成用の関数を作ります。

generate_data <- function(data_description) {
  chat_mall <- chat_google_gemini(
    model = "gemini-2.5-flash",
    system_prompt = "
  ユーザーのリクエストに基づいて10行の表形式のデータを生成してください。
  Rの関数 `readr::read_csv()` で読み込める形式でデータを生成してください。
  
  例えば
TransactionID, Make, Model, Year, Price, Salesesperson
001, Toyota, Camry, 2020, 24000, John Smith
002, Honda, Accord, 2019, 22000, Jane Doe
003, Ford, Explorer, 2021, 32000, Emily Johnson
004, Chevrolet, Malibu, 2020, 23000, Michael Brown
005, Nissan、 Altima、2021年、25000、Sarah Davis
006、Hyundai、Elantra、2019年、19000、David Wilson
007、BMW、X3、2020年、42000、Mary White
008、Audi、A4、2021年、38000、Robert Martinez
009、Subaru、Outback、2020年、27000、James Lee
010、Kia、Soul、2021年、21000、Linda Thompson

  データにはテキスト書式を含めないでください(例:backticksは使用しない)。
  データセットに他の情報を含めないでください。"
)

  csv_string <- chat_mall$chat(data_description, echo = FALSE) 

  readr::read_csv(csv_string, show_col_types = FALSE)
}

ダミーデータの生成。

review_df <- generate_data("
IDとreviewの2列のデータを生成してください。
IDは日本人の姓名、reviewは通販サイトで購入した家電製品に関する購入者によるレビューです。
レビュー内容はポジティブなレビュー、ネガティブなレビュー、ニュートラルなレビューを混ぜてください。
                           ")

生成結果がこちら。

# A tibble: 10 × 2
   ID        review
   <chr>     <chr>
 1 山田 太郎 洗濯機が届きました。静かで洗い上がりも満足です。配送も早く助かりました。
 2 佐藤 花子 電子レンジの温めムラがひどいです。期待外れでした。もう少しパワーが欲しかったです。
 3 鈴木 一郎 炊飯器は普通に美味しいご飯が炊けます。特に不満はありませんが、感動もありません。
 4 高橋 美咲 吸引力が高く、部屋がとてもきれいになりました。コードレスで使いやすいです。
 5 田中 健太 トースターの焼きムラがひどく、パンが焦げ付くことが多いです。デザインは良いのに残念です。
 6 渡辺 結衣 冷蔵庫は容量が大きく使いやすいです。可もなく不可もなくといった感じです。
 7 伊藤 隆   エアコンの効きが良く、今年の夏は快適に過ごせそうです。取り付け業者の方も親切でした。
 8 中村 陽子 ジューサーミキサーのモーター音がうるさく、朝使うのがためらわれます。思っていたよりパワーもありませんでした。
 9 小林 大輔 ドライヤーは風量があり、髪がすぐに乾きます。軽くて持ちやすいのも気に入っています。
10 加藤 咲   電気ケトルはすぐに沸いて便利です。ごく一般的な製品だと思います。

悪くなさそうですね。

ではLLMのモデルを指定します。mallではchatオブジェクトをllm_use()関数で指定する必要があります。

library(mall)
chat <- chat_google_gemini(model = "gemini-2.5-flash")
llm_use(chat)

感情分析

それではこのレビューに対して感情分析をしてみましょう。
今回は単純に"positive", "negative", "neutral"の3区分に分類するものです。

res <- review_df |>
  llm_sentiment(review)

.sentiment列に判定結果が入ります。

# A tibble: 10 × 3
   ID        review                                                                                       .sentiment
   <chr>     <chr>                                                                                        <chr>     
 1 山田 太郎 洗濯機が届きました。静かで洗い上がりも満足です。配送も早く助かりました。                     positive  
 2 佐藤 花子 電子レンジの温めムラがひどいです。期待外れでした。もう少しパワーが欲しかったです。           negative  
 3 鈴木 一郎 炊飯器は普通に美味しいご飯が炊けます。特に不満はありませんが、感動もありません。             neutral   
 4 高橋 美咲 吸引力が高く、部屋がとてもきれいになりました。コードレスで使いやすいです。                   positive  
 5 田中 健太 トースターの焼きムラがひどく、パンが焦げ付くことが多いです。デザインは良いのに残念です。     negative  
 6 渡辺 結衣 冷蔵庫は容量が大きく使いやすいです。可もなく不可もなくといった感じです。                     neutral   
 7 伊藤 隆   エアコンの効きが良く、今年の夏は快適に過ごせそうです。取り付け業者の方も親切でした。         positive  
 8 中村 陽子 ジューサーミキサーのモーター音がうるさく、朝使うのがためらわれます。思っていたよりパワーもありませんでした。…… negative  
 9 小林 大輔 ドライヤーは風量があり、髪がすぐに乾きます。軽くて持ちやすいのも気に入っています。           positive  
10 加藤 咲   電気ケトルはすぐに沸いて便利です。ごく一般的な製品だと思います。                             positive  

悪くなさそうですね。

翻訳

次に日本語のレビューを英語に翻訳してみます。

res <- review_df |>
  llm_translate(review, "英語", pred_name = "訳文") |>
  dplyr::select(!review)

結果です。

# A tibble: 10 × 2
   ID        訳文                       
   <chr>     <chr>                       
 1 山田 太郎 The washing machine arrived quickly, and I am satisfied with its quiet operation and wash results.                         
 2 佐藤 花子 The microwave heats very unevenly, which is disappointing, and it lacks sufficient power.                                  
 3 鈴木 一郎 The rice cooker makes delicious rice, and while satisfactory, it's not particularly impressive.                            
 4 高橋 美咲 The suction power is strong, making the room very clean. It is cordless and easy to use.                                   
 5 田中 健太 The toaster toasts unevenly and often burns the bread, which is a shame given its good design.                             
 6 渡辺 結衣 The refrigerator is spacious and easy to use, but it's nothing special.                                                    
 7 伊藤 隆   The air conditioning works well, ensuring a comfortable summer, and the installers were kind.                              
 8 中村 陽子 The juicer mixer's motor is noisy, making me hesitant to use it in the morning, and it also has less power than I expected.
 9 小林 大輔 The hair dryer has powerful airflow, drying hair quickly. It's also lightweight and easy to hold.                          
10 加藤 咲   Electric kettles are quick and convenient, and are a very common product.     

これは便利です。

要素抽出

最後に要素の抽出です。テキストに含まれる

res <- review_df |>
  llm_extract(review, "製品")

結果です。

# A tibble: 10 × 3
   ID        review                                                                                                       .extract          
   <chr>     <chr>                                                                                                        <chr>             
 1 山田 太郎 洗濯機が届きました。静かで洗い上がりも満足です。配送も早く助かりました。                                     洗濯機            
 2 佐藤 花子 電子レンジの温めムラがひどいです。期待外れでした。もう少しパワーが欲しかったです。                           電子レンジ        
 3 鈴木 一郎 炊飯器は普通に美味しいご飯が炊けます。特に不満はありませんが、感動もありません。                             炊飯器            
 4 高橋 美咲 吸引力が高く、部屋がとてもきれいになりました。コードレスで使いやすいです。                                   掃除機            
 5 田中 健太 トースターの焼きムラがひどく、パンが焦げ付くことが多いです。デザインは良いのに残念です。                     トースター        
 6 渡辺 結衣 冷蔵庫は容量が大きく使いやすいです。可もなく不可もなくといった感じです。                                     冷蔵庫            
 7 伊藤 隆   エアコンの効きが良く、今年の夏は快適に過ごせそうです。取り付け業者の方も親切でした。                         エアコン          
 8 中村 陽子 ジューサーミキサーのモーター音がうるさく、朝使うのがためらわれます。思っていたよりパワーもありませんでした。 ジューサーミキサー
 9 小林 大輔 ドライヤーは風量があり、髪がすぐに乾きます。軽くて持ちやすいのも気に入っています。                           ドライヤー        
10 加藤 咲   電気ケトルはすぐに沸いて便利です。ごく一般的な製品だと思います。                                             電気ケトル   

なかなかやりますね!
これはいろいろ使い勝手が良さそうです。


以上です!
LLMの発展は正に日進月歩ですので、今後も新たなパッケージがどんどこ登場すると思われますが、まずは今回紹介したパッケージを使ってみてはいかがでしょうか。

Enjoy!

13
10
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
13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?