京橋のバイオインフォマティシャンの日常

南国のビーチパラソルの下で、Rプログラムを打ってる日常を求めて、、Daily Life of Bioinformatician in Kyobashi of Osaka

R/Slack APIの諸設定、slackrの使い方、及びGoogle scholarで検索された新着論文情報を知らせるTips

はじめに : R版Slack API

RのSlack APIであるslackrパッケージの使い方について、いろいろとまとめてみた*1

APIの諸設定、基本的なslackrの使い方に加えて、新着論文情報をRからチャネルに送信するプログラムも実装してみた。

まずは、Salck API設定の手順からはじめよう。

Salck API設定の手順

1. Slackのワークスペースに、任意のチャネルを作る。(必要なら)

2. Slack アプリを新規作成する

Rからは以下の関数実行で、Slack APIのサイトに飛べる。

browseURL("https://api.slack.com/apps/new")

ここで、任意のApp Nameを記入して、Development Slack Workspaceを選択する*2

3. Incomming WebhooksをActiveにする

はじめに、Incomming Webhooks の設定に進む。

デフォルトでは、その設定はOFFになっている。

その設定を、ONにする。

次に、Add New Webhook to Workspaceをクリックする

次の画面で、対象となるチャネルを選択して、許可する。

そうすると、Webhook URLが作成される。

ただ、この後のBot Token Scopesを設定して、reinstallすると、新たなWebhook URLが作成されるっぽいので、ひとまずはスルーしておく。

4. Bot User OAuth Tokenの取得、OAuth & Permissionsの設定

次に、サイドバーで、OAuth & Permissionsをクリックする。

ここで、上部に表示される、Bot User OAuth Token は後ほど使用するので、コピーして、保存しておく。

続いて、Bot Token Scopesを設定する。

ここには、incoming-webhookのみが追加されているので、「Add an OAuth Scope」でScopeを追加する。

まずは、以下の項目あたりを追加して、様子をみるのが良い。

  • channels:read

  • chat:write

  • chat:write.customize

  • chat:write.public

  • users:read

  • files:read

  • files:write

  • groups:read

  • groups:write

  • im:read

  • im:write

  • channels:history

上記が選択できたら、reinstallして、次の画面で、対象となるチャネルを選択して、許可する。

そうしたら、Incomming Webhooksの設定に戻って、新しく生成されたWebhook URLを取得しておく。

Sample curl requestのコードの最後部分が最新のWebhook URLなので、それをコピーしておく。

5. Slack アプリをチャネルに招待する【これが重要!】

どうも、以下の設定は必須みたい。

Known Issues

Depending on your scopes, slackr could quietly fail (i.e. not throw an error, but also not post anything to your channel). If this happens, try explicitly adding the app you’re trying to have slackr post as to the channel you want in your Slack workspace with /invite @your_app_name or make sure you have chat:write.public enabled.

日本語への機械翻訳: 既知の問題

スコープによっては、slackrが静かに失敗することがあります(エラーをスローせず、チャンネルに何もポストしないなど)。この問題が発生した場合は、Slackrに投稿させようとしているアプリをSlackのワークスペースに/invite @your_app_nameで明示的にチャンネルに追加するか、chat:write.publicを有効にしてください。

これは、chat:write.publicを有効にするだけでは解決しない。

やることは、対象チャネルで、「/invite @App Name」を入力して、作成したSlack アプリをチャネルに加える。

以下は、「@r-slack」を追加したときの例である。

送信すれば、「@r-slack」がチャネルに招待される。

6. RでのSlack APIの設定

ようやく、slackr側の設定を行う。

Rを起動して、まずは関連パッケージをインストール・ロードする。

#パッケージのインストール
library(devtools)
devtools::install_github("hrbrmstr/slackr", force = TRUE)
library(slackr)

7. slackr config ファイルの設定

ここでは、Rからホームディレクトリに、~/.slackrを作成する。

上記で、コピー・保存しておいたOAuth TokenやWebhook URLを使用する。

.slackrの基本フォーマットは以下の通りである。

#基本的なフォーマット
bot_user_oauth_token: Bot User OAuth Token
channel: #チャネル名
username: 上記のApp Name
incoming_webhook_url: https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX

以下、R上から~/.slackrの作成を行う。tokenやチャネル名は、適時変更のこと。

また、~/.slackrを設定さえすれば、次回使用時からはslackr_setup()だけで完結する。

#Token & チャネル名 & Username の設定
OAuth_token <- "xoxb-XXXXXXX-XXXXXXX-XXXXXXX"
WebURL <- "https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX"
Channel <- "チャネル名"
#チャネル名は、"Channel ID"でも可 
Username <- "slack-r"
#Usernameは任意でOK

a <- paste0("
bot_user_oauth_token: ", OAuth_token, "
channel: ", Channel, "
username: ", Username, "
incoming_webhook_url: ", WebURL)

#作成
system(paste0("echo \"", a, "\" > ~/.slackr"))

#確認
system("cat ~/.slackr")

#削除 (必要なら)
#system("rm -rf ~/.slackr")

#slackr_setupの実行
slackr_setup()
#[1] "Successfully connected to Slack"

あと、オプション的な内容である。

#authentication & identity のチェック
auth_test()

#Slack usersの表示
slackr_users()

#Slack channelsの表示
slackr_channels()

#設定状況の確認
Sys.getenv("SLACK_CHANNEL")
Sys.getenv("SLACK_BOT_USER_OAUTH_TOKEN")

8. 簡単な投稿実行

次に、実際に、Slackrパッケージを使って見ていこう。

#メッセージ送信
slackr_msg("Test message")

#メッセージをR表現の結果 「```形式」 として送信
slackr_bot('Test message')

上記の出力結果

#Rスクリプトの実行結果の送信
slackr(summary(UKgas))

#Plot結果を送信する
plot(UKgas)
slackr_dev(channels="#test")
#OR
ggslackr(plot(UKgas))

#画像のアップロード: 任意の画像で試してください。
slackr_upload("./01.png")

#チャネルを指定して送信する場合
library(maps)
map('county')
slackr_dev(channels = "#test")

#その他
#チャネルからメッセージを削除する
#slackr_delete(count=1)

上記の出力結果(一部)

R/Slackrで、Google scholarで検索された新着論文情報を知らせる

応用例として、Google scholarで検索された論文情報を新着順に10件取得して、Slackのチャネルに投げるということをやってみる。

Rを用いたWebスクレイピングの詳細については、以下の記事を参考のこと。

skume.net

また、googleScholarSearchNewest関数については、下記の補足資料を参照のこと。

#パッケージとかの準備
if(!require("rvest")){install.packages("rvest")}; library(rvest)
if(!require("xml2")){install.packages("xml2")}; library(xml2)
if(!require("magrittr")){install.packages("magrittr")}; library(magrittr)
source("https://gist.githubusercontent.com/kumeS/ef4d750a191b847174b3b93a4b8391c9/raw/9281e95919903fa66f2c43b71b9ae2340404d8a8/googleSearch_function.R")

#検索語の準備 ex. electron microscopy
Query <- c("electron microscopy")

#Google Scholar 検索実行
d <- googleScholarSearchNewest(Query=Query)

#結果ベクトルの取得
res <- d$`electron microscopy`

res
#[1] "https://www.nature.com/articles/s41467-021-21709-z"                          
# [2] "https://pubs.acs.org/doi/abs/10.1021/acsmacrolett.1c00032"                   
# [3] "https://academic.oup.com/cdn/advance-article/doi/10.1093/cdn/nzab025/6174670"
# [4] "https://pubs.acs.org/doi/full/10.1021/acsnano.0c10065"                       
# [5] "https://pubs.acs.org/doi/abs/10.1021/acsnano.0c10065"                        
# [6] "https://www.sciencedirect.com/science/article/abs/pii/S2213138821001284"     
# [7] "https://link.springer.com/article/10.1140/epjp/s13360-021-01291-5"           
# [8] "https://pubs.acs.org/doi/abs/10.1021/acs.energyfuels.1c00167"                
# [9] "https://journals.sagepub.com/doi/abs/10.1177/0095244321996396"               
#[10] "https://www.sciencedirect.com/science/article/pii/S0269749121005315"  

#Slackへの出力
for(n in 1:length(res)){
slackr_msg(paste0("Search results No. ", n))
slackr_msg(res[n])
}

Slack上に、こんな感じで出力される

まとめ

ファイルのアップロードとか、スクリプトの情報共有とかは楽なのかもしれない。

今のところ、メッセージ送信とかレスポンスとかを、Rからすべてやるのはちょっとやり過ぎかもという所感である。

補足資料

googleScholarSearchNewest関数について

gist.github.com

参考資料

qiita.com

github.com

*1:Web上の日本語資料の2021版アップデート

*2:Slackにログインしておくとよいかも

【R・ビッグデータ解析の処方箋】Rで、10万ノードを超える大きなネットワーク図を描画するTips 〜 igraph::plot.igraphは使い物にならない件 〜

はじめに

Rでのネットワーク図の作成では、igraph packageがよく使われる。

ただ、igraphによるネットワーク図の描写は、1万ノードを超えたあたりから、結構な時間がかかる。

そのため、10万ノードを超えるような、大規模なネットワーク図の描画には、ちょっとしたコツがある。

今回、そんな大規模なネットワークの作成方法を取り上げ、実際の実行時間を検証してみた。

結論的には、igraph::plot.igraphを使うよりも、graphics::plot.defaultで描画する方が、10倍以上早くなる。

graphics::plot.defaultによるネットワーク図の作成例

まずは、Barabasi-Albertモデルで、サンプルのグラフ構造を作成する。

#パッケージ準備
library(igraph)

#サンプル・グラフの作成
#sample_pa: Barabasi-Albertモデルによるスケールフリー・グラフの生成関数
#BAモデルは、グラフを構築するためのシンプルな確率的アルゴリズムである。
pa <- igraph::sample_pa(n=10, power=1, m=1, directed=F)

pa
#IGRAPH fe31e44 U--- 10 9 -- Barabasi graph
#+ attr: name (g/c), power (g/n), m (g/n), zero.appeal (g/n),
#| algorithm (g/c)
#+ edges from fe31e44:
#[1] 1-- 2 2-- 3 2-- 4 2-- 5 1-- 6 3-- 7 1-- 8 5-- 9 7--10

#graphics::plot.defaultによる作図
system.time(graphics::plot.default(layout_with_fr(pa), pch=20, cex=2.5,
                       axes = F,  type = "p", xlab = NA, ylab = NA))
#quartz.save(file = paste0("./Graph_net_00.png"), type = "png", dpi = 150); dev.off()

うーん、この図だと、ネットワークのエッジがない。

そこで、線分を追加するsegments関数で、エッジの描画を行う。

関数として組むと、こんな感じである。

gist.github.com

このNetwork_plot関数で、pa を描画してみる。

#パッケージの準備 & 簡単な実行例
library(data.table)
library(magrittr)
source("https://gist.githubusercontent.com/kumeS/2c8204b1c8a78a16d00ec2eaed5a7ca5/raw/e67a5760d7fcfc3a9c8f62f729e407b2722f5bf2/Network_plot.R")

pa <- igraph::sample_pa(n=10, power=1, m=1, directed=F)
Network_plot(pa, Cex=2.5)
#quartz.save(file = paste0("./Graph_net_01.png"), type = "png", dpi = 150); dev.off()

見慣れた、ネットワーク図が得られる。

グラフのレイアウトは、layout_with_fr *1 を使用している。

グラフ・ノード数による描画時間の比較

それでは、次に、グラフのノード数を増やしていって、igraph::plot.igraphとgraphics::plot.defaultを使う場合とでの実行時間を比較してみる。

検証したスクリプトは、以下の通りである。

#####################
#10 nodes
#####################
#plot.igraphでの実行
pa <- sample_pa(n=10, power=1, m=1, directed=F)
system.time(plot.igraph(pa, vertex.size=10, vertex.label=NA))
#   ユーザ   システム       経過  
#     0.014      0.002      0.019 
#quartz.save(file = paste0("./Graph_01.png"), type = "png", dpi = 150); dev.off()

#Network_plotでの実行
system.time(Network_plot(pa, Cex=2.5))
#   ユーザ   システム       経過  
#     0.016      0.002      0.018 
#quartz.save(file = paste0("./Graph_02.png"), type = "png", dpi = 150); dev.off()

10 nodes by plot.igraph

10 nodes by Network_plot

#####################
#100 nodes
#####################
#plot.igraphでの実行
pa <- sample_pa(n=100, power=1, m=1, directed=F)
system.time(plot.igraph(pa, vertex.size=4, vertex.label=NA))
#   ユーザ   システム       経過  
#     0.023      0.003      0.026
#quartz.save(file = paste0("./Graph_03.png"), type = "png", dpi = 150); dev.off()

#Network_plotでの実行
system.time(Network_plot(pa, Cex=1.5))
#   ユーザ   システム       経過  
#     0.029      0.003      0.031
#quartz.save(file = paste0("./Graph_04.png"), type = "png", dpi = 150); dev.off()

100 nodes by plot.igraph

100 nodes by Network_plot

#####################
#1000 nodes
#####################
pa <- sample_pa(n=1000, power=1, m=1, directed=F)
system.time(plot(pa, vertex.size=2, vertex.label=NA))
#   ユーザ   システム       経過  
#     1.033      0.017      1.053 
#quartz.save(file = paste0("./Graph_05.png"), type = "png", dpi = 150); dev.off()

#Network_plotでの実行
system.time(Network_plot(pa, Cex=0.5))
#   ユーザ   システム       経過  
#     0.771      0.006      0.781 
#quartz.save(file = paste0("./Graph_06.png"), type = "png", dpi = 150); dev.off()

1000 nodes by plot.igraph

1000 nodes by Network_plot

#####################
#10000 nodes: 1万ノード
#####################
pa <- sample_pa(n=10000, power=1, m=1, directed=F)
system.time(plot(pa, vertex.size=1, vertex.label=NA))
#   ユーザ   システム       経過  
#    11.014      0.035     11.061
#quartz.save(file = paste0("./Graph_07.png"), type = "png", dpi = 150); dev.off()

#Network_plotでの実行
system.time(Network_plot(pa, Cex=0.2))
#   ユーザ   システム       経過  
#     0.873      0.005      0.881
#quartz.save(file = paste0("./Graph_08.png"), type = "png", dpi = 150); dev.off()

10000 nodes by plot.igraph

10000 nodes by Network_plot

#####################
#100000 nodes: 10万ノード
#####################
pa <- sample_pa(n=100000, power=1, m=1, directed=F)
system.time(plot(pa, vertex.size=0.5, vertex.label=NA))
#   ユーザ   システム       経過  
#   140.286      0.545    141.231
#quartz.save(file = paste0("./Graph_09.png"), type = "png", dpi = 150); dev.off()

#Network_plotでの実行
system.time(Network_plot(pa, Cex=0.03))
#   ユーザ   システム       経過  
#     9.381      0.021      9.423
#quartz.save(file = paste0("./Graph_10.png"), type = "png", dpi = 150); dev.off()

100000 nodes by plot.igraph

100000 nodes by Network_plot

#####################
#200000 nodes: 20万ノード
#####################
pa <- sample_pa(n=200000, power=1, m=1, directed=F)
system.time(plot(pa, vertex.size=0.5, vertex.label=NA))
#   ユーザ   システム       経過  
#   278.272      0.539    279.129
#quartz.save(file = paste0("./Graph_11.png"), type = "png", dpi = 150); dev.off()

#Network_plotでの実行
system.time(Network_plot(pa, Cex=0.01))
#   ユーザ   システム       経過  
#    20.613      0.037     20.700 
#quartz.save(file = paste0("./Graph_12.png"), type = "png", dpi = 150); dev.off()

200000 nodes by plot.igraph

200000 nodes by Network_plot

実行時間の可視化

実行時間を表で比較すると、1000や10000あたりから、すでに差が出てくる。

ノード数 igraph::plot.igraphでの実行時間 Network_plotでの実行時間
10 0.019 0.018
100 0.026 0.031
1000 1.053 0.781
10000 11.061 0.881
100000 141.231 9.423
200000 279.129 20.700

図としてプロットすると、こんな感じ。

tm <- data.frame(a=c(10, 100, 1000, 10000, 100000, 200000),
                 b=c(0.019,0.026,1.053,11.061,141.231,279.129),
                 c=c(0.018,0.031,0.781,0.881,9.423,20.700))
par(family="HiraKakuProN-W3", mgp=c(2.5, 1, 0), mai=c(0.75, 0.75, 0.5, 0.25))
plot(tm[,1], tm[,2], type="n", log="x", xlab="nodes", ylab="Time")
points(tm[,1], tm[,2], type="b", pch=16)
points(tm[,1], tm[,3], type="b", pch=21)
legend("topleft", legend=c("igraph::plot.igraphでの実行時間","Network_plotでの実行時間"), pch=c(16, 21), cex=1)
#quartz.save(file = paste0("./Graph_13.png"), type = "png", dpi = 150); dev.off()

まとめ

描画速度が歴然と違う、、、igraphオブジェクトは1万ノードくらいを扱うのが良いところかな。

大規模なネットワークを扱いたい人には、graphics::plot.defaultが基本型だろう。

参考資料

stackoverflow.com

*1:Fruchterman and ReingoldによるForce-Directed Layoutアルゴリズムを用いて、平面上に頂点を配置するレイアウト。

【Rでの文字列処理シリーズ(その2)】文字列ベクトルの連結と文字列長のカウント

はじめに

文字列処理・テキスト処理とは、プログラミングを行うなかで、文字列・テキストに対する色々な操作のことを指します。それら処理をうまく使いこなすことで、文字列を自由に処理できるようになります。文字列処理の活用事例は、キーワード抽出、テキスト分類、テキストマイニングの前処理など、多岐に渡ります。 今回の「Rでの文字列処理」シリーズで扱う、文字列処理のライブラリ・関数群やプログラムコードは、R環境上で無料で提供されている、オープン・ソフトウェアを用います。

この記事では、Rでの文字列ベクトルの連結とカウントについて、いろいろと試してまとめてみました。 主に、baseやstringrとかのパッケージ内の関数群を扱います。 文字列の連結・結合は普段もよく使うので簡単と思ってたけど、奥が深い一面も垣間見れて、良かったです。

テキスト処理の関連記事

skume.net

skume.net

skume.net

skume.net

skume.net

skume.net

下準備について

########################
#シリーズ共通
########################
#必要なパッケージの読み込み
require(readr)
require(stringr)
require(stringdist)

#テストファイルのダウンロード
utils::download.file(url="https://raw.githubusercontent.com/kumeS/Blog/master/TXT_proc/test.txt",
                     destfile="test.txt")

【1】文字列ベクトルの連結

base::paste0 関数・ stringr::str_c 関数

base::paste0、stringr::str_c についての実行例を示す。

paste0関数は、paste関数の引数 sep = ""が標準出力の関数である。

#入力ベクトル
abc <- c("A", "B", "C", "D", "E")

# 使用する引数
# sep: 入力ベクトル間に挿入する文字列
# collapse: 入力ベクトルを1つの文字列に連結するために使用される文字列

#base::paste0での実行例
#paste0での標準出力
base::paste0(abc)
#[1] "A" "B" "C" "D" "E"

#ベクトルの各要素の後ろに「,」を付与する
base::paste0(abc, sep=",")
#[1] "A," "B," "C," "D," "E,"

#ただ、base::pasteでは同じ出力とはならない
base::paste(abc, sep=",")
#[1] "A" "B" "C" "D" "E"  => この場合、処理効果なし

#文字列ベクトルを1つの文字列として連結する
base::paste0(abc, collapse="")
#[1] "ABCDE"

#ベクトルの各要素の間に「,」を付与して、文字列ベクトルを1つの文字列として連結する
base::paste0(abc, collapse=",")
#[1] "A,B,C,D,E"

次に、ベクトルではなく、文字列を並べたときの出力結果を見ていく。

#"A", "B", "C", "D", "E"と、文字列として並べたときのデフォルト出力
base::paste0("A", "B", "C", "D", "E")
#[1] "ABCDE"

#sep=","を設定すると、最後にコンマが付く
base::paste0("A", "B", "C", "D", "E", sep=",")
#[1] "ABCDE,"

#ただ、base::pasteでは同じ出力とはならず、間にコンマが入る
base::paste("A", "B", "C", "D", "E", sep=",")
#[1] "A,B,C,D,E"

#collapse=","を設定すると
base::paste0("A", "B", "C", "D", "E", collapse=",")
#[1] "ABCDE"  => この場合、処理効果なし

stringr::str_c 関数

続いて、stringr::str_c関数での実行例を見ていく。

#デフォルト出力
stringr::str_c(abc)
#[1] "A" "B" "C" "D" "E"

#sep=","の設定にすると
stringr::str_c(abc, sep=",")
#[1] "A" "B" "C" "D" "E"  => この場合、処理効果なし

#collapse=","の設定にすると、間にコンマが入る
stringr::str_c(abc, collapse=",")
#[1] "A,B,C,D,E"

#ベクトルではなく、文字列を並べたときの出力
stringr::str_c("A", "B", "C", "D", "E")
#[1] "ABCDE"

#sep=","の設定にすると、間にコンマが入る
stringr::str_c("A", "B", "C", "D", "E", sep=",")
#[1] "A,B,C,D,E"

#collapse=","の設定にすると、
stringr::str_c("A", "B", "C", "D", "E", collapse=",")
#[1] "ABCDE"  => この場合、処理効果なし

関数によって、sep、collapseの引数としての影響が若干違ってくるみたい。

【2】文字列長のカウント

文字列長をカウントする関数として、 base::nchar関数、stringr::str_length関数、stringr::str_count関数を取り上げる。

base::nchar 関数

#文字列長のカウント: ベクトルでの実行
#入力ベクトル
abc <- c("A", "B", "C", "D", "E")

#文字列長のカウント: 文字列での実行
base::nchar(abc)
#[1] 1 1 1 1 1

#文字列長のカウント: データフレームでの実行
#入力データフレーム
c <- data.frame(readr::read_tsv(file="./test.txt", col_names=F))
c
#           X1
#1 abc ABC abc
#2 ABC abc ABC
#3 def DEF def
#4 DEF def DEF
#5 abc.ABC.abc
#6 ABC.abc.ABC

#データフレームに含まれる全文字数のカウント
base::nchar(c)
#X1 
#91

#要素ごとでの文字数のカウント
base::nchar(c$X1)
#[1] 11 11 11 11 11 11

少し横道にそれるが、apply関数とbase::nchar関数とを組み合わせる場合もやってみる。

#apply関数を組み合わせた文字数のカウント
#MARGIN = 1 (rows)でapply実行する
apply(c, MARGIN=1, base::nchar)
#[1] 11 11 11 11 11 11

#MARGIN = 2 (columns)でapply実行する
apply(c, MARGIN=2, base::nchar)
#     X1
#[1,] 11
#[2,] 11
#[3,] 11
#[4,] 11
#[5,] 11
#[6,] 11

MARGIN=1の場合に、横に出力されて、 MARGIN=2の場合に、縦に出力されるのがやや違和感あるけど。。

次に、データフレームを分割して、3x6のデータフレームで、文字数のカウントをやってみる。

行列で入力するか、データフレームで入力するかで、どうも出力結果が変わるみたい。

#分割した行列での実行
c2 <- stringr::str_split_fixed(c$X1, pattern=c(" ", " ", " ", " ", "[.]", "[.]"), n=3)
c2
#     [,1]  [,2]  [,3] 
#[1,] "abc" "ABC" "abc"
#[2,] "ABC" "abc" "ABC"
#[3,] "def" "DEF" "def"
#[4,] "DEF" "def" "DEF"
#[5,] "abc" "ABC" "abc"
#[6,] "ABC" "abc" "ABC"

#要素ごとの文字列長のカウント
base::nchar(c2)
#     [,1] [,2] [,3]
#[1,]    3    3    3
#[2,]    3    3    3
#[3,]    3    3    3
#[4,]    3    3    3
#[5,]    3    3    3
#[6,]    3    3    3
#セルごとでカウントされる

#分割したデータフレームでの実行
c3 <- data.frame(stringr::str_split_fixed(c$X1, pattern=c(" ", " ", " ", " ", "[.]", "[.]"), n=3))
c3
#   X1  X2  X3
#1 abc ABC abc
#2 ABC abc ABC
#3 def DEF def
#4 DEF def DEF
#5 abc ABC abc
#6 ABC abc ABC

base::nchar(c3)
#X1 X2 X3 
#43 43 43 
#列ごとでカウントされる。

stringr::str_length 関数 ・ stringr::str_count 関数

次に、stringr::str_length関数やstringr::str_count関数でカウントしてみた。どちらの関数も、行列を入力としたところ、ベクトルで出力された。

#stringr::str_lengthでのカウント実行: 文字列の長さをベクトルで出力
stringr::str_length(c2)
#[1] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3

#文字列マッチでのカウント: 大文字あるいは小文字のアルファベット数でカウント
stringr::str_count(c2, pattern = "[a-z]|[A-Z]")
#[1] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3

補足

formatC関数を使って、出力文字列の桁数を揃える

d <- 1:10
d
#[1]  1  2  3  4  5  6  7  8  9 10

#「0」を加えて、桁数3桁で揃える。
formatC(d, width = 3, flag = "0")
#[1] "001" "002" "003" "004" "005" "006" "007"
#[8] "008" "009" "010"

連番数値を作成する

d1 <- seq_len(10)
d1
#[1]  1  2  3  4  5  6  7  8  9 10

大文字 <=> 小文字の変換

小文字 => 大文字への変換では、toupper関数、str_to_upper関数(ベクトル化される)を使用する。また、大文字 => 小文字への変換では、tolower関数、str_to_lower関数(ベクトル化される)を使用する。

#大文字・小文字が混在する行列を使用する
c2
#     [,1]  [,2]  [,3] 
#[1,] "abc" "ABC" "abc"
#[2,] "ABC" "abc" "ABC"
#[3,] "def" "DEF" "def"
#[4,] "DEF" "def" "DEF"
#[5,] "abc" "ABC" "abc"
#[6,] "ABC" "abc" "ABC"

#大文字への変換
toupper(c2)
#     [,1]  [,2]  [,3] 
#[1,] "ABC" "ABC" "ABC"
#[2,] "ABC" "ABC" "ABC"
#[3,] "DEF" "DEF" "DEF"
#[4,] "DEF" "DEF" "DEF"
#[5,] "ABC" "ABC" "ABC"
#[6,] "ABC" "ABC" "ABC"

#小文字への変換
tolower(c2)
#     [,1]  [,2]  [,3] 
#[1,] "abc" "abc" "abc"
#[2,] "abc" "abc" "abc"
#[3,] "def" "def" "def"
#[4,] "def" "def" "def"
#[5,] "abc" "abc" "abc"
#[6,] "abc" "abc" "abc"

#大文字ベクトルへの変換
stringr::str_to_upper(c2)
# [1] "ABC" "ABC" "DEF" "DEF" "ABC" "ABC" "ABC"
# [8] "ABC" "DEF" "DEF" "ABC" "ABC" "ABC" "ABC"
#[15] "DEF" "DEF" "ABC" "ABC"

#小文字ベクトルへの変換
stringr::str_to_lower(c2)
# [1] "abc" "abc" "def" "def" "abc" "abc" "abc"
# [8] "abc" "def" "def" "abc" "abc" "abc" "abc"
#[15] "def" "def" "abc" "abc"

参考資料

skume.net

skume.net

https://cran.r-project.org/web/packages/readr/readr.pdf

https://cran.r-project.org/web/packages/stringr/stringr.pdf

cran.r-project.org

github.com