闪亮蔚蓝
您是否要在Twitter上跟踪会议的主题标签(或任何主题标签)? 该奖金“用R实现更多功能”教程逐步向您展示如何创建交互式Shiny Web应用程序,以使用特定的主题标签(或任何关键字)搜索,排序和过滤推文。
如果您不想阅读整个Twitter R教程,则可以免费下载 Insider额外的Shiny和R应用程序代码以及R Markdown和PDF格式的本文 。
[ 在InfoWorld的“用R做更多”的视频系列中获取Sharon Machlis的R技巧 | 按任务,主题或程序包搜索“用R做更多”操作视频 ]
原始的R代码来搜索推文
《用R做更多》第41集演示了如何使用rtweet包搜索tweet以及如何创建具有reactable的可排序,可过滤表。 该文章的第一个代码块搜索推文并格式化结果以显示在表中。
# Configure variables: number of tweets to download and hashtag search query
num_tweets_to_download <- 200
hashtag_to_search <- "#rstudioconf"
# Make sure to install any packages listed below that you don't already have on your system:
library("rtweet")
library("reactable")
library("glue")
library("stringr")
library("httpuv")
library("dplyr")
library("purrr")
# Code to actually search for tweets
tweet_df <- search_tweets(hashtag_to_search, n = num_tweets_to_download, include_rts = FALSE)
# select a few desired columns and add a clickable link to tweet text for table data
tweet_table_data <- tweet_df %>%
select(user_id, status_id, created_at, screen_name, text, favorite_count, retweet_count, urls_expanded_url) %>%
mutate(
Tweet = glue::glue("{text} <a href='https://twitter.com/{screen_name}/status/{status_id}'>>> </a>")
)%>%
select(DateTime = created_at, User = screen_name, Tweet, Likes = favorite_count, RTs = retweet_count, URLs = urls_expanded_url)
下一个代码块是使tweet URL列中的URL可单击的功能。 正如我在主要片段中提到的那样,这比创建一条推文链接要复杂一些,因为每条推文可能有多个URL。 生成可点击的URL可能是一种更为优雅的方法,但是下面的功能可以正常工作。
make_url_html <- function(url) {
if(length(url) < 2) {
if(!is.na(url)) {
as.character(glue("<a title = {url} target = '_new' href = '{url}'>{url}</a>") )
} else {
""
}
} else {
paste0(purrr::map_chr(url, ~ paste0("<a title = '", .x, "' target = '_new' href = '", .x, "'>", .x, "</a>", collapse = ", ")), collapse = ", ")
}
}
tweet_table_data$URLs <- purrr::map_chr(tweet_table_data$URLs, make_url_html)
第三个也是最后一个代码块创建一个交互式可React表。 此块中的大多数代码都会调整默认样式和表行为。
reactable::reactable(tweet_table_data,
filterable = TRUE, searchable = TRUE, bordered = TRUE, striped = TRUE, highlight = TRUE,
showSortable = TRUE, defaultSortOrder = "desc", defaultPageSize = 25, showPageSizeOptions = TRUE, pageSizeOptions = c(25, 50, 75, 100, 200),
columns = list(
DateTime = colDef(defaultSortOrder = "asc"),
User = colDef(defaultSortOrder = "asc"),
Tweet = colDef(html = TRUE, minWidth = 190, resizable = TRUE),
Likes = colDef(filterable = FALSE, format = colFormat(separators = TRUE)),
RTs = colDef(filterable = FALSE, format = colFormat(separators = TRUE)),
URLs = colDef(html = TRUE)
)
)
创建一个基本的Shiny应用程序以搜索推文
现在,让我们将此数据表转换为具有更多过滤功能的本地交互式Shiny应用程序。
首先,转到RStudio,然后通过转到文件>新建文件>新建Web应用程序,在项目的新子目录中创建一个新的Shiny应用程序 。 我称呼我的应用程序HashtagSearch,但您可以随便叫什么。
RStudio创建的默认app.R文件如下所示:
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
# Run the application
shinyApp(ui = ui, server = server)
为您的Shiny应用程序创建UI
闪亮的应用程序的UI部分是用户界面或与用户(在本例中为您)进行交互的前端HTML页面。 服务器部分是后端R代码 ,它执行所有计算以使用户的请求真正起作用。
通常,我通常首先删除默认app.R文件中的大多数注释,更改标题,然后加载我需要的所有软件包。 我的app.R的顶部现在看起来像这样:
library(shiny)
library(rtweet)
library(dplyr)
library(glue)
library(reactable)
library(purrr)
接下来,我将添加用于搜索推文的代码,将其导入R,并纠缠要显示在表中的版本-但我将对它进行一点点炫耀。
我知道我要使用我的URL HTML格式功能。 我将其放在所有交互代码之前的应用程序顶部。 (或者,我可以将其放在单独的帮助程序文件中并提供该文件的源。)我的应用程序顶部现在看起来像这样:
library(shiny)
library(rtweet)
library(dplyr)
library(glue)
library(reactable)
library(purrr)
make_url_html <- function(url) {
if(length(url) < 2) {
if(!is.na(url)) {
as.character(glue("<a title = {url} target = '_new' href = '{url}'>{url}</a>") )
} else {
""
}
} else {
paste0(purrr::map_chr(url, ~ paste0("<a title = '", .x, "' target = '_new' href = '", .x, "'>", .x, "</a>", collapse = ", ")), collapse = ", ")
}
}
ui <- fluidPage(
接下来,我将添加代码以通过搜索查询创建初始Twitter数据框。 那是在app.R的服务器部分。 由于该数据框将根据用户的要求进行更改和更新,因此我需要使其为React性值 。
到目前为止,用户可以更改两个项目:主题标签搜索查询和要返回的推文数量。 我不想将这些值硬编码在文件顶部,而是将它们添加为用户可以更改的输入。
这意味着在app.R UI部分中添加两个用户输入字段: hashtag_to_search和num_tweets_to_download 。 我将创建一个文本框用于输入主题标签,并创建数字输入框或滑块以输入要请求的推文数量。
对于推文的数量,我将从保留默认app.R中附带的滑块开始。 但是,我将其ID更改为num_tweets_to_download ,将用户看到的标签更改为“ 要下载的tweets数量” ,将最小值更改为100,将最大值更改为18000,将默认值更改为200,并执行步骤(滑块的每次轻微移动都会增加或减少100)。
我还将要删除行server <- function(input, output) {### DELETE EVERYTHING IN HERE### }
括号之间的服务器部分中的所有内容server <- function(input, output) {### DELETE EVERYTHING IN HERE### }
因为默认应用现在无法正常工作了我正在更改输入。
现在,应用程序的UI和服务器部分如下所示:
ui <- fluidPage(
# Application title
titlePanel("Search tweets"),
# Sidebar
sidebarLayout(
sidebarPanel(
sliderInput("num_tweets_to_download",
"Number of tweets to download:",
min = 100,
max = 18000,
value = 30,
step = 100)
),
# Show results
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic
server <- function(input, output) {
output$distPlot <- renderPlot({
})
}
如果我通过单击RStudio窗格右上方的Run App按钮运行该应用程序(图2),则可以看到我的滑块(尽管它还没有执行任何操作)。
如果发现在移动滑块时很难获得所需的确切值,则可以将该滑块更改为numericInput()
框:
numericInput("num_tweets_to_download",
"Number of tweets to download:",
min = 100,
max = 18000,
value = 200,
step = 100)
这将为您提供一个您可以键入而不是滑块的框(图3)。
接下来,我将使用Shiny的textInput()
函数为主题标签查询添加一个文本输入框。 我将框的inputId设置为hashtag_to_search ,标签设置为“要搜索的标签”,并将默认值设置为“ #rstudioconf” 。 我的侧边栏面板代码现在看起来像这样:
# Sidebar
sidebarLayout(
sidebarPanel(
numericInput("num_tweets_to_download",
"Number of tweets to download:",
min = 100,
max = 18000,
value = 200,
step = 100), # <- Don’t forget comma here
textInput("hashtag_to_search",
"Hashtag to search:",
value = "#rstudioconf")
),
请注意,由于我要在侧边栏面板中添加第二个输入参数,因此在第一个输入参数之后需要一个逗号,如上面的注释箭头所示。 在编写Shiny应用程序时,使括号和逗号正确可能是一个挑战 。 逐一创建输入和输出并在每次添加后运行代码可以帮助您更轻松地发现问题。
单击以运行最新的代码,您应该看到类似图4的内容。
基本的侧边栏已完成,但我需要在UI中再输入一个信息,以告诉Shiny我希望在其中显示推文表。 我将占位符放在主面板中。 默认应用程序的主面板具有plotOutput("distPlot")
,它告诉plotOutput("distPlot")
节省页面主要部分中的绘图空间。 我在那里需要一个可React的表 ,因此我将plotOutput()
更改为plotOutput()
reactableOutput()
。 更具体地说,我将表inputId设置为“ tweet_table”并交换reactableOutput("tweet_table")
。 如果单击立即运行我的应用程序,则没有任何改变。 但是我们快到了。
编码Shiny服务器逻辑
现在是时候真正获得并显示推文了。 就像原始代码一样,我将使用rtweet的search_tweet()
函数来搜索tweet,并将结果存储在tweets_df (将包含90列)中。 然后,我将从结果中创建一个tweets_table_data数据框,对其进行格式化以显示在表格中,并对表格进行编码。 所有这些服务器逻辑都必须位于app.R文件的server <- function(input, output) { }
部分内部。
我可以通过一些修改来重复使用原始代码。 最重要的一点:
- 根据用户输入而变化的任何变量都必须是React性值 ,而不是常规R对象。 与传统变量相比,我必须以略有不同的方式来创建和处理它们,但是很高兴地,这些调整很小。
- 每当我需要用户输入的值时,都必须将其称为i
nput$my_variable_name
而不是my_variable_name
。
让我们看看它是如何工作的。
这是我的非发光代码,用于搜索推文并为表创建结果的版本:
tweet_df <- search_tweets(hashtag_to_search, n = num_tweets_to_download, include_rts = FALSE)
# select a few desired columns, add clickable links for tweets and URLs, rename some columns
tweet_table_data <- tweet_df %>%
select(user_id, status_id, created_at, screen_name, text, favorite_count, retweet_count, urls_expanded_url) %>%
mutate(
Tweet = glue::glue("{text} <a href='https://twitter.com/{screen_name}/status/{status_id}'>>> </a>"),
URLs = purrr::map_chr(urls_expanded_url, make_url_html)
)%>%
select(DateTime = created_at, User = screen_name, Tweet, Likes = favorite_count, RTs = retweet_count, URLs)
要将“常规”值转换为React性值,请使用Shiny的reactive({})
函数。 请记住:要引用由用户与应用程序的交互作用设置的变量hashtag_to_search
和num_tweets_to_download
,请使用input$var_name
而不是var_name
。 那变成了这个非发光的代码:
tweet_df <- search_tweets(hashtag_to_search, n = num_tweets_to_download, include_rts = FALSE)
进入此闪亮代码:
tweet_df <- reactive({
search_tweets(input$hashtag_to_search, n = input$num_tweets_to_download, include_rts = FALSE)
})
以及tweet_table_data代码:
tweet_table_data <- reactive({
req(tweet_df())
tweet_df() %>%
select(user_id, status_id, created_at, screen_name, text, favorite_count, retweet_count, urls_expanded_url) %>%
mutate(
Tweet = glue::glue("{text} <a href='https://twitter.com/{screen_name}/status/{status_id}'>>> </a>"),
URLs = purrr::map_chr(urls_expanded_url, make_url_html)
)%>%
select(DateTime = created_at, User = screen_name, Tweet, Likes = favorite_count, RTs = retweet_count, URLs)
})
仅创建tweet_table_data数据帧的前三行已更改。 第一行,t weet_table_data <- reactive({
,创建一个可以根据用户输入进行更改的对象。第二行, req(tweet_df())
,告诉Shiny不要在tweet_df()数据帧之前开始任何tweet_table_data的计算。 不存在这一行,如果我的应用尝试在不存在的数据帧上运行计算,则我的应用可能会抛出错误,也请注意,它是tweet_df()
而不是tweet_df
因为该数据帧是React性的。
除了结尾处的})
之外,其余代码与之前相同。
最后,表代码需要进行三处小的更改:
- 由于表格是可视化的,因此Shiny需要知道将其放置在何处以及它是什么 。 我将表存储在名为
output$tweet_table
的变量中。 它将它与用户界面中的reactableOutput("tweet_table")
占位符reactableOutput("tweet_table")
。 我还将使用renderReactable()
函数,以便Shiny知道它正在创建哪种可视化。 这使得代码output$tweet_table <- renderReactable({ ### R code to create the reactable table here ### })
。 注意:大多数时候,当您想在Shiny中使用可视化时,您会在UI中添加诸如myfunctionOutput("my_dataviz_id")
,并output$my_dataviz_id <- renderMyfunction({ ### R code to create viz here ### })
。
- 该代码应要求存在tweet_table_data数据框,否则该应用可能会尝试从不存在的数据生成表时引发错误。
- 代码也需要将
tweet_table_data
称为tweet_table_data()
因为它是React性的。
闪亮表代码:
output$tweet_table <- renderReactable({
reactable::reactable(tweet_table_data(),
filterable = TRUE, searchable = TRUE, bordered = TRUE, striped = TRUE, highlight = TRUE,
showSortable = TRUE, defaultSortOrder = "desc", defaultPageSize = 25, showPageSizeOptions = TRUE, pageSizeOptions = c(25, 50, 75, 100, 200),
columns = list(
DateTime = colDef(defaultSortOrder = "asc"),
User = colDef(defaultSortOrder = "asc"),
Tweet = colDef(html = TRUE, minWidth = 190, resizable = TRUE),
Likes = colDef(filterable = FALSE, format = colFormat(separators = TRUE)),
RTs = colDef(filterable = FALSE, format = colFormat(separators = TRUE)),
URLs = colDef(html = TRUE)
)
)
})
这是整个app.R Shiny app的代码:
library(shiny)
ui <- fluidPage(
# Application title
titlePanel("Search tweets"),
# Sidebar
sidebarLayout(
sidebarPanel(
numericInput("num_tweets_to_download",
"Number of tweets to download:",
min = 100,
max = 18000,
value = 200,
step = 100),
textInput("hashtag_to_search",
"Hashtag to search:",
value = "#rstudioconf")
),
# Show results
mainPanel(
reactableOutput("tweet_table")
)
)
)
# Define server logic
server <- function(input, output) {
tweet_df <- reactive({
search_tweets(input$hashtag_to_search, n = input$num_tweets_to_download, include_rts = FALSE)
})
tweet_table_data <- reactive({
req(tweet_df())
tweet_df() %>%
select(user_id, status_id, created_at, screen_name, text, favorite_count, retweet_count, urls_expanded_url) %>%
mutate(
Tweet = glue::glue("{text} <a href='https://twitter.com/{screen_name}/status/{status_id}'>>> </a>"),
URLs = purrr::map_chr(urls_expanded_url, make_url_html)
)%>%
select(DateTime = created_at, User = screen_name, Tweet, Likes = favorite_count, RTs = retweet_count, URLs)
})
output$tweet_table <- renderReactable({
reactable::reactable(tweet_table_data(),
filterable = TRUE, searchable = TRUE, bordered = TRUE, striped = TRUE, highlight = TRUE,
showSortable = TRUE, defaultSortOrder = "desc", defaultPageSize = 25, showPageSizeOptions = TRUE, pageSizeOptions = c(25, 50, 75, 100, 200),
columns = list(
DateTime = colDef(defaultSortOrder = "asc"),
User = colDef(defaultSortOrder = "asc"),
Tweet = colDef(html = TRUE, minWidth = 190, resizable = TRUE),
Likes = colDef(filterable = FALSE, format = colFormat(separators = TRUE)),
RTs = colDef(filterable = FALSE, format = colFormat(separators = TRUE)),
URLs = colDef(html = TRUE)
)
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
运行该应用程序,瞧! 它看起来应该像这样:
一些实际的改进
我想对此应用程序进行一些改进,使其更加实用。 首先是应用程序如何在用户更改默认的主题标签或要请求的推文数量时做出React。 默认情况下,Shiny会在您输入时尝试更新数据。 这通常是很酷且有用的行为。 但是在这种情况下,我不希望应用程序每次输入字母或数字时都调用Twitter API! 否则,它可能会在每次击键后尝试返回对部分请求的搜索-我冒着撞到15分钟内18,000条推文的风险。
我想添加的另一个功能是保存我的应用返回的一些数据。
对于第一个问题,在单击“获取数据”按钮之前 ,我不希望我的应用向Twitter API发出请求。 RStudio建议为此功能使用“操作按钮”。 与Shiny中的大多数内容一样,这涉及两个部分:UI和服务器。
对于UI,我可以使用actionButton()
函数添加一个动作按钮。 第一个参数是按钮的inputId。 第二个是用户看到的按钮标签。 有一些可选参数,例如用于更改按钮外观的内置类。 我喜欢btn-primary CSS类,因此将其用于按钮: actionButton("get_data", "Get data", class = "btn-primary")
。
为了使该按钮执行某项操作(或更准确地说,是停止应用程序执行某项操作,直到所有输入信息准备就绪),我需要将tweet_df()
对象从响应值更改为响应事件 。 这意味着它不只是根据用户输入的值来更改其值,还可以通过用户执行的操作 (例如单击按钮)来更改其值。 服务器中的语法如下:
my_reactive_object <- eventReactive(input$my_button_id,
{
### R code here ###
})
注意输入$ my_button_id之后的逗号! 我花了很多时间来尝试查找Shiny错误,因为我忘记了React函数的第一个参数之后的逗号。
接下来,将以下行添加到UI sidebarPanel:
actionButton("get_data", "Get data", class = "btn-primary")
现在,整个面板应如下所示:
sidebarPanel(
numericInput("num_tweets_to_download",
"Number of tweets to download:",
min = 100,
max = 18000,
value = 200,
step = 100),
textInput("hashtag_to_search",
"Hashtag to search:",
value = "#rstudioconf"),
actionButton("get_data", "Get data", class = "btn-primary")
),
并从此更改服务器代码中的tweet_df定义:
tweet_df <- reactive({
search_tweets(input$hashtag_to_search, n = input$num_tweets_to_download, include_rts = FALSE)
})
对此:
tweet_df <- eventReactive(input$get_data, {
search_tweets(input$hashtag_to_search, n = input$num_tweets_to_download, include_rts = FALSE)
})
如果您现在运行该应用程序,则在单击“获取数据”按钮之前,什么都不会发生。
要将数据保存到本地计算机,可以添加下载数据按钮。 再一次,我们需要UI和服务器中的代码。
为了使下载按钮出现在应用程序中,有一个特殊的downloadButton()
函数,其语法为downloadButton("id", "Label")
。 我的按钮代码包括两个br()
换行符,用于将按钮与上面的项目分开,然后是:
br(),br(),
downloadButton("download_data", "Download data")
现在,完整的侧边栏面板代码为:
sidebarPanel(
numericInput("num_tweets_to_download",
"Number of tweets to download:",
min = 100,
max = 18000,
value = 200,
step = 100),
textInput("hashtag_to_search",
"Hashtag to search:",
value = "#rstudioconf"),
actionButton("get_data", "Get data", class = "btn-primary"),
br(),br(),
downloadButton("download_data", "Download data")
),
如果您现在运行app.R代码,它将下载HTML页面,这当然不是您想要的行为。 为了使服务器逻辑真正使按钮下载数据,您需要使用Shiny的downloadHandler()
函数。 此功能需要两条信息:您要为下载的文件名和您要下载的数据。 它使用以下格式。 请注意,需要具有文件名和下载内容的功能:
output$download_data <- downloadHandler(
filename = function() {
paste(input$hashtag_to_search, "_", Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(tweet_table_data(), file, row.names = FALSE)
}
)
将该地方添加到app.R服务器代码中,然后尝试一下。
请注意,您在表格中所做的任何过滤都不会反映在您下载的数据中。 另外,您可能希望下载完整的数据集-在这种情况下, tweet_df()
包含90列数据,而不是tweet_table_data()
-因为您永远都不知道将来要使用会议tweet进行什么样的分析。 在这种情况下,只需将write.csv(tweet_table_data(), file, row.names = FALSE)
更改为write.csv(tweet_df(), file, row.names = FALSE)
。
附加过滤
加载Twitter数据后,您可以进行更多过滤。 Shiny输入窗口小部件库显示了可以添加到Shiny应用程序的许多用户输入选项。
例如,您可以使用日期范围过滤器仅在会议日期期间查看推文,以筛选出会议前和会议后的聊天记录。
为此,UI代码使用诸如dateRangeInput("my_date_picker_id", label = "Select dates:", start = "2020-01-27", end = "2020-01-30")
,其中开始和结束是默认值。
尝试将该代码添加到UI侧边栏面板代码中的某个位置(选择所需的日期并始终记住正确的逗号),并根据需要添加br()
换行符。 请注意,您的默认日期也可以是Sys.Date() - 7
以“七天前”开始。
我的侧面板代码现在看起来像这样:
sidebarPanel(
numericInput("num_tweets_to_download",
"Number of tweets to download:",
min = 100,
max = 18000,
value = 200,
step = 100),
textInput("hashtag_to_search",
"Hashtag to search:",
value = "#rstudioconf"),
dateRangeInput("date_picker", label = "Select dates:", start = "2020-01-27", end = "2020-01-30"),
actionButton("get_data", "Get data", class = "btn-primary"),
br(),br(),
downloadButton("download_data", "Download data")
),
生成侧边栏面板,如图8所示。
要实际过滤日期范围选择器,请进入tweet_table_data()
定义,然后添加代码以根据DateTime列过滤行。 在这种情况下,由于日期选择器的inputId为“ date_picker”,因此起始值为input$date_picker[1]
,结束值为input$date_picker[2]
。
我将在tweet_table_data()
定义中添加一行,以过滤在用户请求的开始日期和结束日期之间创建的推文:
filter(between(as.Date(created_at), input$date_picker[1], input$date_picker[2]))
创建React性tweet_table_data()
数据框架的代码现在如下所示:
tweet_table_data <- reactive({
req(tweet_df())
tweet_df() %>%
select(user_id, status_id, created_at, screen_name, text, favorite_count, retweet_count, urls_expanded_url) %>%
filter(between(as.Date(created_at), input$date_picker[1], input$date_picker[2]) ) %>%
mutate(
Tweet = glue::glue("{text} <a href='https://twitter.com/{screen_name}/status/{status_id}'>>> </a>"),
URLs = purrr::map_chr(urls_expanded_url, make_url_html)
)%>%
select(DateTime = created_at, User = screen_name, Tweet, Likes = favorite_count, RTs = retweet_count, URLs)
})
[ 通过InfoWorld的机器学习和分析报告时事通讯来掌握机器学习,人工智能和大数据分析的最新进展 ]
更多增强
您可以做更多的事情来增加此应用程序的功能。 您可能会添加滑块以设置最少数量的转发或点赞,然后对那部分推文进行其他表过滤。 或者,您可以添加一个复选框以筛选包含URL的推文-如果您正在寻找可能包含指向演示文稿和其他资源的链接的会议推文,则可能很有用。 您可以保留初始数据集中的其他列,以过滤语言和地理位置等变量。
要了解有关Shiny的更多信息,RStudio在Shiny.rstudio.com上提供了一些书面和视频教程。 其他有用的资源:Zev Ross 用示例和备忘单创建了一个易于遵循的Shiny教程 ,Hadley Wickham正在进行中的Mastering Shiny书籍正在网上免费提供。
有关rtweet的更多信息,请访问rtweet.info 。
有关使用R的更多技巧,请访问InfoWorld的“ 用R进行更多操作”页面!
闪亮蔚蓝