Yusuke Matsui
Biomedical and Health Informatics Unit (BMHI),
Department of Integrated Health Sciences,
Graduate School of Medicine,
Nagoya University, Systems Biology Division,
Integrated Glycan-Big Data Center (iGDATA),
Institute for Glycobiology Core Research (iGCORE)
インフォマティクス技術を活用してデータを収集し、解析した結果を具体的な形で利用者に提示するには、わかりやすいインターフェースが必要である。そのため、ウェブアプリケーションは、データと利用者を結びつける重要なインターフェースとなる。特に、健康科学や医療、ケアの分野では、専門的な解析結果を分かりやすく提供する手段として、ウェブアプリケーションが広く利用されている。
R Shinyは、Rのデータ解析能力とウェブアプリケーションを組み合わせたツールであり、これを用いることで、複雑な統計解析結果やデータ可視化を簡単に操作・表示できるようにすることができる。
健康科学、医療、ケアの現場におけるウェブアプリケーションの役割は、次のような側面から考察できる:
次に、ウェブアプリケーションを使って解決できるシンプルな課題をいくつか考え、その解決策を議論する。
演習1. 体重の変動をリアルタイムに可視化するアプリケーション
演習2. 文献情報の自動要約アプリケーション
演習3. ユーザーデータをフィルタリングして結果をダウンロードするアプリケーション
演習4. データのキュレーションと簡易データベースの構築
次に、上記の課題に対してどのような解析フローが必要かを考え、それをRで実装する手順を議論する。
ggplot2
パッケージなどを活用して、データの可視化を行う。ShinyアプリケーションにおけるUI(ユーザーインターフェース)とは、主にブラウザーを通じてユーザーが直接操作する部分を指す。例えば、通常のウェブページでボタンをクリックしたり、フォームにデータを入力したりするのと同じように、Shinyアプリケーションでもブラウザー上でこれらの操作を行う。具体的には、Shinyアプリケーションを実行すると、ユーザーが見る画面に日付を選んだり、体重を入力したりするためのフィールドや、グラフが表示される部分が現れる。これがUIにあたる。
一方で、Server とは、裏で動いている部分で、ユーザーがブラウザーで入力したデータを受け取り、処理を行う部分である。この処理には、データの保存、計算、分析などが含まれる。Server側の処理は、ユーザーが入力した内容に応じて自動的に実行され、その結果がUI、つまりブラウザー上にリアルタイムで反映される。
例えば、Shinyアプリケーションの体重管理システムを使う際の具体的な流れを見てみよう。
ブラウザー(UI)に表示される画面
初めに、ユーザーがブラウザーを通じてアプリケーションを開くと、画面には日付選択ボタンと体重を入力するフィールドが表示されている。また、これまでに入力された体重データを表示するグラフも表示されている。
ユーザーの入力
ユーザーが日付を選び、体重を入力し、ボタンを押すと、そのデータはブラウザーから「Server」に送られる。ユーザーはこの時、実際にはブラウザーを介してシステムにデータを送信していることになる。
Serverでの処理
Server側では、入力された体重データを既存のデータに追加し、保存したり、グラフを更新したりする処理が自動的に行われる。この処理は、ユーザーが「データを追加」というボタンをクリックするたびに実行される。
結果の反映
Serverが処理を終えると、処理された結果(例えば、更新された体重データのグラフ)が再びブラウザー上に表示される。これが、Shinyアプリケーションが持つリアクティブ性、つまりユーザーの入力に応じてデータが更新され、結果が即座に表示される仕組みである。
仮想データを用いたコード演習講義を構築するために、シンプルなデータを扱いながら、R Shinyアプリケーションを用いたデータの入力、可視化、解析の基本的な流れを体験する。
次に示すのは、体重管理をテーマにした仮想データを使用したR Shinyアプリケーションのコードであり、体重データの入力、グラフによる可視化、体重の増減傾向の解析を行うものである。
install.packages("shiny")
install.packages("ggplot2")
# ライブラリの読み込み
library(shiny)
library(ggplot2)
# 仮想的な体重データ
initial_data <- data.frame(
date = Sys.Date() - 9:0,
weight = c(70, 69.8, 69.5, 69.7, 69.9, 70.1, 70.3, 70.2, 70.0, 70.2)
)
# UI部分
ui <- fluidPage(
titlePanel("体重管理アプリケーション"),
sidebarLayout(
sidebarPanel(
h4("体重データの入力"),
dateInput("date", "日付を選択", value = Sys.Date()),
numericInput("weight", "体重 (kg)", value = 70, min = 30, max = 150, step = 0.1),
actionButton("add", "データを追加"),
hr(),
h4("過去1週間の体重変化"),
textOutput("trend")
),
mainPanel(
h4("体重変化のグラフ"),
plotOutput("weightPlot")
)
)
)
# サーバー部分
server <- function(input, output, session) {
# 体重データをリアルタイムで保存するためのデータフレーム
data <- reactiveVal(initial_data)
# データ追加ボタンが押されたときの処理
observeEvent(input$add, {
new_data <- data.frame(date = input$date, weight = input$weight)
updated_data <- rbind(data(), new_data)
data(updated_data)
})
# 体重変化グラフの表示
output$weightPlot <- renderPlot({
ggplot(data(), aes(x = date, y = weight)) +
geom_line(color = "blue") +
geom_point(color = "red") +
labs(x = "日付", y = "体重 (kg)", title = "体重の変化") +
theme_minimal()
})
# 体重変化の傾向を計算
output$trend <- renderText({
recent_data <- tail(data(), 7)
if (nrow(recent_data) < 2) {
return("データが足りません。")
} else {
diff <- recent_data$weight[nrow(recent_data)] - recent_data$weight[1]
if (diff > 0) {
return(paste("過去1週間で", round(diff, 2), "kg増加しました。"))
} else if (diff < 0) {
return(paste("過去1週間で", round(abs(diff), 2), "kg減少しました。"))
} else {
return("過去1週間で体重に変化はありません。")
}
}
})
}
# Shinyアプリケーションを起動
shinyApp(ui = ui, server = server)
まず、Shinyを使わずに、基本的なデータ処理や可視化を行う。ここでは、仮想的なデータを生成し、ggplot2
を使った可視化を行う。
まず、簡単なデータフレームを作成し、基本的な操作を紹介する。
# データフレームの作成
data <- data.frame(
Category = c("A", "B", "C", "D"),
Values = c(23, 45, 12, 56)
)
# データの表示
print(data)
# データのサマリー表示
summary(data)
ggplot2
を使って基本的な可視化を行う。
# ggplot2のインストール
install.packages("ggplot2")
library(ggplot2)
# 棒グラフの作成
ggplot(data, aes(x = Category, y = Values)) +
geom_bar(stat = "identity") +
theme_minimal()
次に、Shinyを使わずにデータ処理を自動化してみよう。たとえば、ユーザー入力をコードに反映させる部分を実行することで、どの部分がインタラクティブにできるかを考える。
たとえば、異なるフィルタをデータに適用する操作を実行させ、ユーザーの選択に応じた処理を行う。
# 特定のカテゴリのデータだけを選択して表示
selected_category <- "A" # ここをユーザー入力に見立てる
filtered_data <- subset(data, Category == selected_category)
# フィルタ後のデータを表示
print(filtered_data)
ここで、インタラクティブにする意味について説明する。たとえば、ユーザーが自分でフィルタやグラフの設定を変更できると、リアルタイムで結果が反映されることで、データ分析がより直感的かつ効果的になることを強調できると考えられる。Shinyによるコードの書き方について、簡潔に説明を追加したいと思います。以下のステップに沿って、Shinyの構造と書き方の基本を紹介する。
ここでは、Shinyアプリの構造と、アプリの動作を実現するためのコードの書き方を学ぶ。Shinyは、UI(ユーザーインターフェース) と Server(サーバー側の処理) の2つの要素で構成されている。
Shinyアプリは次のように、ui
(ユーザーインターフェース)とserver
(サーバー側の処理)をそれぞれ定義し、それをshinyApp()
関数でアプリとして起動する。
UI(ユーザーインターフェース): これは、ユーザーがアプリに入力する要素(テキストボックス、プルダウンメニュー、ボタンなど)や、結果を表示するための領域(グラフ、テーブルなど)を定義。
Server(サーバー側の処理): ここでは、UIからの入力に基づいてデータの処理や計算を行い、その結果をUIに表示するための関数を定義。
shinyApp()
関数:
この関数を使って、ui
と server
を組み合わせてアプリを起動。
# Shinyパッケージのインストールと読み込み
install.packages("shiny")
library(shiny)
# UIの定義
ui <- fluidPage(
titlePanel("Shinyによる簡単なインタラクティブアプリ"),
sidebarLayout(
sidebarPanel(
selectInput("category", "カテゴリを選んでください", choices = unique(data$Category))
),
mainPanel(
plotOutput("barPlot")
)
)
)
# Serverの定義
server <- function(input, output) {
output$barPlot <- renderPlot({
filtered_data <- subset(data, Category == input$category) # 入力に基づきデータをフィルタ
ggplot(filtered_data, aes(x = Category, y = Values)) +
geom_bar(stat = "identity") +
theme_minimal()
})
}
# Shinyアプリとして起動
shinyApp(ui = ui, server = server)
fluidPage()
:
ページ全体のレイアウトを定義する関数である。Shinyアプリは主にこの関数でUIを作成する。
titlePanel()
:
アプリのタイトルを表示するためのエリアである。
sidebarLayout()
:
サイドバーとメイン表示エリアのレイアウトを定義する。
sidebarPanel()
:
ユーザーの入力を受け付けるためのサイドバーエリアを定義する。ここでは、selectInput()
を使ってユーザーが選択できるカテゴリを定義する。mainPanel()
:
サイドバーに対応するメイン表示エリア。ここではplotOutput()
でグラフを表示する領域を指定する。server
関数:
ユーザーの入力に基づいて処理を行うサーバー側のロジックを定義する。
renderPlot()
:
グラフを動的に作成し、plotOutput()
で定義されたエリアに表示する。shinyApp()
:
UIとサーバーを結びつけ、アプリを実行する。
この演習では、研究者が文献検索に費やす時間を削減し、関連する情報を効率よく取得できるようにする自動化システムを構築することを目的とする。
以下に、演習の手順を示す。
PubMedは生物医学分野の膨大な文献を収集しているデータベースで、APIを通じて自動的に論文データを取得することができる。APIを利用して、特定のキーワードに関連する論文を取得し、その要旨を抜き出すプログラムを作成する。
必要な準備: - PubMed APIへのアクセス(NCBI
E-utilities)を使用。 - 必要なRパッケージのインストール
(httr
, jsonlite
など)。
以下に、指定したキーワードに基づいてPubMedから論文の要旨を取得し、自動的に要約するプログラムの基本的な流れを示す。
# 必要なパッケージをインストール
install.packages("httr")
install.packages("jsonlite")
install.packages("tm")
# ライブラリの読み込み
library(httr)
library(jsonlite)
library(tm)
# PubMed APIを使って、キーワードに関連する論文を検索する関数
search_pubmed <- function(keyword, max_results = 10) {
# PubMedのAPIエンドポイント
base_url <- "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
# APIクエリの作成
query <- list(
db = "pubmed",
term = keyword,
retmax = max_results,
retmode = "json"
)
# APIリクエストの送信
response <- GET(base_url, query = query)
# レスポンスをJSON形式に変換
result <- fromJSON(content(response, "text"), flatten = TRUE)
# PubMed ID (PMID) のリストを取得
pmids <- result$esearchresult$idlist
return(pmids)
}
# PMIDを使って、要約を取得する関数
fetch_abstracts <- function(pmids) {
base_url <- "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi"
abstracts <- list()
for (pmid in pmids) {
query <- list(
db = "pubmed",
id = pmid,
retmode = "json"
)
response <- GET(base_url, query = query)
result <- fromJSON(content(response, "text"), flatten = TRUE)
# 論文の要約を取得
abstract <- result$result[[pmid]]$title
abstracts[[pmid]] <- abstract
}
return(abstracts)
}
# 要約を自動的に短縮する関数 (単純な要約方法)
summarize_text <- function(text) {
# テキストを前処理して、最初の数文を抽出
sentences <- unlist(strsplit(text, split = "\\.")) # ピリオドで分割
summary <- paste(sentences[1:min(3, length(sentences))], collapse = ". ")
return(summary)
}
# メインの処理
keyword <- "genome sequencing"
pmids <- search_pubmed(keyword, max_results = 5)
abstracts <- fetch_abstracts(pmids)
# 要約の表示
for (pmid in names(abstracts)) {
cat("PMID:", pmid, "\n")
cat("Original Abstract:\n", abstracts[[pmid]], "\n\n")
cat("Summary:\n", summarize_text(abstracts[[pmid]]), "\n\n")
}
keyword
に特定のトピックを設定する(例: “genome
sequencing”)。このキーワードに関連する文献をPubMed
APIから取得する。Shinyを使って、文献検索と要約の自動化を行うアプリケーションを実装するためには、ユーザーインターフェース(UI)でキーワードを入力し、検索結果を取得して表示する仕組みを組み込む必要がある。また、要約結果をインタラクティブに表示する部分も設ける。以下は、Shinyを使った基本的な構成例である。
まず、必要なパッケージをインストールする。
install.packages("shiny")
install.packages("httr")
install.packages("jsonlite")
以下は、文献検索と要約の自動化をShinyで実装するコード例である。
# ライブラリの読み込み
library(shiny)
library(httr)
library(jsonlite)
# PubMed APIを使って、キーワードに関連する論文を検索する関数
search_pubmed <- function(keyword, max_results = 10) {
base_url <- "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
query <- list(
db = "pubmed",
term = keyword,
retmax = max_results,
retmode = "json"
)
response <- GET(base_url, query = query)
result <- fromJSON(content(response, "text"), flatten = TRUE)
pmids <- result$esearchresult$idlist
return(pmids)
}
# PMIDを使って、要約を取得する関数
fetch_abstracts <- function(pmids) {
base_url <- "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi"
abstracts <- list()
for (pmid in pmids) {
query <- list(
db = "pubmed",
id = pmid,
retmode = "json"
)
response <- GET(base_url, query = query)
result <- fromJSON(content(response, "text"), flatten = TRUE)
abstract <- result$result[[pmid]]$title
abstracts[[pmid]] <- abstract
}
return(abstracts)
}
# 要約を自動的に短縮する関数
summarize_text <- function(text) {
sentences <- unlist(strsplit(text, split = "\\.")) # ピリオドで分割
summary <- paste(sentences[1:min(3, length(sentences))], collapse = ". ")
return(summary)
}
# UI部分
ui <- fluidPage(
titlePanel("PubMed 文献検索と要約"),
sidebarLayout(
sidebarPanel(
textInput("keyword", "検索キーワードを入力", value = "genome sequencing"),
numericInput("max_results", "取得する論文数", value = 5, min = 1, max = 100),
actionButton("search", "文献検索")
),
mainPanel(
h4("検索結果"),
tableOutput("abstracts")
)
)
)
# サーバー部分
server <- function(input, output, session) {
# 検索結果のデータフレームを保持するためのリアクティブオブジェクト
search_results <- reactiveVal(data.frame(PMID = character(), Abstract = character(), Summary = character(), stringsAsFactors = FALSE))
# 検索ボタンが押されたときの処理
observeEvent(input$search, {
# 文献検索を実行
pmids <- search_pubmed(input$keyword, input$max_results)
abstracts <- fetch_abstracts(pmids)
# データフレームにPMID、要約、サマリーを保存
result_df <- data.frame(
PMID = names(abstracts),
Abstract = unlist(abstracts),
Summary = sapply(abstracts, summarize_text),
stringsAsFactors = FALSE
)
search_results(result_df) # 結果をリアクティブに保存
})
# 結果の表示
output$abstracts <- renderTable({
search_results()
})
}
# Shinyアプリケーションの起動
shinyApp(ui = ui, server = server)
textInput()
を使ってユーザーが検索キーワードを入力する。numericInput()
で取得する論文数を設定する。actionButton()
を使って検索をトリガーする。observeEvent()
で検索ボタンが押されたときにPubMedのAPIにアクセスし、関連するPMIDを取得し、要約を取得する。summarize_text()
関数で要約を短縮し、結果として表示する。renderTable()
を使って検索結果を表示する。テーブルにはPMID、Abstract(要約の原文)、Summary(簡単なサマリー)が表示される。演習のためのデータをダウンロードする。
# matファイルのダウンロード
download.file("https://github.com/ymatts/health_informatics/blob/main/blood_pressure_data.mat?raw=true",
destfile = "blood_pressure_data.mat")
# jsonファイルのダウンロード
download.file("https://github.com/ymatts/health_informatics/blob/main/blood_pressure_data.json?raw=true",
destfile = "blood_pressure_data.json")
# csvファイル1のダウンロード
download.file("https://github.com/ymatts/health_informatics/blob/main/blood_pressure_data.csv?raw=true",
destfile = "blood_pressure_data.csv")
# csvファイル2のダウンロード
download.file("https://github.com/ymatts/health_informatics/blob/main/virtual_patient_data.csv?raw=true",
destfile = "virtual_patient_data.csv")
# xlsxファイルのダウンロード
download.file("https://github.com/ymatts/health_informatics/blob/main/meta_data.xlsx?raw=true",
destfile = "meta_data.xlsx")
Shinyアプリで表形式のデータをアップロードし、データの内容をDT
パッケージを使って表示・フィルタリングし、さらに再度ダウンロードできるアプリケーションのコード例である。
install.packages("shiny")
install.packages("DT")
# 必要なライブラリの読み込み
library(shiny)
library(DT)
# UI部分
ui <- fluidPage(
titlePanel("データアップロードとダウンロードのアプリ"),
sidebarLayout(
sidebarPanel(
fileInput("file1", "CSVファイルをアップロードしてください", accept = c(".csv")),
downloadButton("downloadData", "フィルタリングしたデータをダウンロード")
),
mainPanel(
DTOutput("table")
)
)
)
# サーバー部分
server <- function(input, output) {
# アップロードされたデータを保持するための変数
data <- reactive({
req(input$file1) # ファイルがアップロードされていることを確認
df <- read.csv(input$file1$datapath)
return(df)
})
# アップロードされたデータを表形式で表示
output$table <- renderDT({
req(data())
datatable(data(), filter = "top", options = list(pageLength = 10))
})
# フィルタリングされたデータをダウンロードできるようにする
output$downloadData <- downloadHandler(
filename = function() {
paste("filtered_data", Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(data(), file, row.names = FALSE)
}
)
}
# Shinyアプリを起動
shinyApp(ui = ui, server = server)
fileInput()
ウィジェットを使用。downloadButton()
を設置。reactive()
関数を使って、アップロードされたデータを読み込んで保持する。renderDT()
でアップロードされたデータを表形式で表示し、filter = "top"
オプションを設定して列ごとにフィルタリングを可能にしている。downloadHandler()
を使って、フィルタリングされたデータをダウンロードできるように設定。簡単なデータベースを作成してみよう。まずはデータキュレーションから始まる。このデータは、いわゆるインターネット上の文献補助情報やレポジトリから入手するところから始まる。入手する部分にも、データのインクルージョン / エクスクルージョン基準の策定、データの包括的な体系的検索、目的に沿ったデータかどうかを検証する文献・メタデータの調査、場合によってはデータ利用のための倫理審査、利用申請やダウンロードが含まれる。これらはデータの質を担保する重要なステップの一つである。今回はそのステップは時間的制約からスキップしてダウンロードしたところから始まる。取得したデータの異なるフォーマットのデータを取り扱い、整理・標準化するプロセス、それらを誰もが自由に閲覧し、可視化、ダウンロードできるウェブアプリケーション(つまりデータベース)の作成プロセスを演習する。
まず、JSON、CSV、MATLAB形式のデータを読み込み、それらを共通のデータ形式に変換する必要がある。Rでは、以下のパッケージを使用。
jsonlite
:JSONファイルを読み込むためのパッケージreadr
:CSVファイルを読み込むためのパッケージR.matlab
:MATLAB形式のデータを読み込むためのパッケージreadxl
:Excelのメタデータを読み込むためのパッケージinstall.packages("shiny")
install.packages("DT")
install.packages("jsonlite")
install.packages("readr")
install.packages("R.matlab")
install.packages("readxl")
install.packages("dplyr")
install.packages("readr")
install.packages("readxl")
データの列名や構造が異なる場合、これを統一して標準化する。以下に仮想的なコード例を示す。
library(jsonlite)
library(dplyr)
# JSONファイルの読み込み
json_data <- fromJSON("blood_pressure_data.json")
# 必要な列名に変換
json_data <- json_data %>%
rename(date = Date, systolic = SystolicBP, diastolic = DiastolicBP)
saveRDS(json_data,"database/blood_pressure_data_json.rds")
library(readr)
# CSVファイルの読み込み
csv_data <- read_csv("blood_pressure_data.csv")
# 必要な列名に変換
csv_data <- csv_data %>%
rename(date = MeasurementDate, systolic = SBP, diastolic = DBP)
saveRDS(csv_data,"database/blood_pressure_data_csv.rds")
library(R.matlab)
# MATLABファイルの読み込み
matlab_data <- readMat("blood_pressure_data.mat")
# 必要なデータフレームに変換
mat_data <- data.frame(
date = as.Date(matlab_data$Date[,1]),
systolic = matlab_data$Systolic,
diastolic = matlab_data$Diastolic
)
saveRDS(mat_data,"database/blood_pressure_data_mat.rds")
library(readxl)
# メタデータの読み込み
meta_data <- read_excel("meta_data.xlsx")
saveRDS(meta_data,"database/meta_data.rds")
以下はShinyアプリケーションを用いて、標準化されたデータを閲覧、フィルタリング、ダウンロードするためのコード。
library(shiny)
library(DT)
library(jsonlite)
library(R.matlab)
library(readxl)
# UI部分
ui <- fluidPage(
titlePanel("血圧データのキュレーションと可視化"),
sidebarLayout(
sidebarPanel(
selectInput("dataset", "データセットを選択してください",
choices = c("JSON形式の血圧データ",
"CSV形式の血圧データ",
"MAT形式の血圧データ",
"Excel形式の血圧データ")),
downloadButton("downloadData", "フィルタリングしたデータをダウンロード")
),
mainPanel(
DTOutput("table")
)
)
)
# サーバー部分
server <- function(input, output) {
# データを読み込む反応関数
data <- reactive({
switch(input$dataset,
"JSON形式の血圧データ" = {
readRDS("blood_pressure_data_json.rds")
},
"CSV形式の血圧データ" = {
readRDS("blood_pressure_data_csv.rds")
},
"MAT形式の血圧データ" = {
readRDS("blood_pressure_data_mat.rds")
},
"Excel形式の血圧データ" = {
readRDS("meta_data.rds")
})
})
# アップロードされたデータを表示
output$table <- renderDT({
req(data())
datatable(data(), filter = "top", options = list(pageLength = 10))
})
# フィルタリングされたデータをダウンロード
output$downloadData <- downloadHandler(
filename = function() {
paste("filtered_data", Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(data(), file, row.names = FALSE)
}
)
}
# Shinyアプリケーションの起動
shinyApp(ui = ui, server = server)
```