เปลี่ยนเอกสาร R Markdown ให้เป็นประสบการณ์เชิงโต้ตอบ

R Markdown เป็นหนึ่งในสิ่งที่ฉันชอบเกี่ยวกับ R สมัยใหม่มันมีวิธีง่ายๆในการรวมข้อความรหัส R และผลลัพธ์ของรหัส R ในเอกสารเดียว และเมื่อเอกสารนั้นแสดงผลเป็น HTML คุณสามารถเพิ่มการโต้ตอบของผู้ใช้กับวิดเจ็ต HTML เช่น DT สำหรับตารางหรือแผ่นพับสำหรับแผนที่ (หากคุณไม่คุ้นเคยกับ R Markdown คุณสามารถดูวิดีโอแนะนำ R Markdown ของฉันก่อนแล้วค่อยกลับมาที่นี่) 

แต่คุณอาจไม่รู้ว่ามีวิธีเพิ่มประสิทธิภาพการโต้ตอบ R Markdown ให้มากยิ่งขึ้น: โดยการเพิ่มruntime: shinyลงในส่วนหัวของเอกสาร

Shiny เป็นเฟรมเวิร์กแอปพลิเคชันบนเว็บสำหรับ R ในฐานะเฟรมเวิร์กมีโครงสร้างที่ค่อนข้างเฉพาะเจาะจง อย่างไรก็ตามคุณสามารถแปลงเอกสาร R Markdown เป็นแอป Shiny ได้โดยไม่ต้องทำตามโครงสร้างที่เข้มงวดมากนัก แต่คุณสามารถเริ่มเขียนโค้ดได้ทันทีโดยไม่ต้องกังวลเกี่ยวกับงาน Shiny ทั่วไปเช่นการตรวจสอบว่าวงเล็บและเครื่องหมายจุลภาคทั้งหมดของคุณถูกต้องภายในฟังก์ชันเค้าโครงที่ซ้อนกันอย่างลึกซึ้ง

ในความเป็นจริงแม้ว่าคุณจะเป็นนักพัฒนาที่มีประสบการณ์ แต่เอกสาร R Markdown ก็ยังมีประโยชน์สำหรับงานที่เป็นประกายซึ่งคุณไม่จำเป็นต้องใช้แอปพลิเคชันเต็มรูปแบบหรือสำหรับการลองใช้โค้ดอย่างรวดเร็ว ยังคงต้องใช้เซิร์ฟเวอร์ Shiny แต่ถ้าคุณติดตั้ง RStudio และแพคเกจเงาคุณก็มีหนึ่งในนั้นในเครื่องแล้ว

มาดูกันว่ารันไทม์มันวาวทำงานอย่างไรใน R Markdown

1. พื้นฐาน R Markdown

ฉันจะเริ่มต้นด้วยเอกสารธรรมดาที่ไม่ใช่ Shiny R Markdown ซึ่งมีตารางข้อมูลที่ค้นหาได้ด้วยรหัสไปรษณีย์ของรัฐแมสซาชูเซตส์ ผู้ใช้สามารถค้นหาหรือจัดเรียงตามคอลัมน์ตารางใดก็ได้โดยตอบคำถามเช่น“ รหัสไปรษณีย์ใดที่มีรายได้เฉลี่ยครัวเรือนสูงสุดของ Middlesex County” หรือ“ รหัสไปรษณีย์ใดที่มีที่อยู่อาศัยรายเดือนที่แพงที่สุด”

ชารอนมัคลิส /

เอกสารนี้ยังมีฮิสโตแกรมที่แสดงการกระจายรายได้เฉลี่ยของครัวเรือนและข้อความที่ระบุว่ารหัสไปรษณีย์ใดมีรายได้สูงสุดและต่ำสุด ตารางเป็นแบบโต้ตอบ แต่ส่วนที่เหลือของเอกสารไม่เป็น คุณสามารถดูเวอร์ชัน HTML ที่แสดงผลได้บน RPub ของ RStudio

หากคุณต้องการทำตามคุณสามารถดูรหัสสำหรับเอกสาร R Markdown เวอร์ชันสแตนด์อะโลนรวมถึงข้อมูลบน GitHub หรือหากคุณต้องการดูว่าฉันนำข้อมูลประชากรนี้ไปเป็น R ได้อย่างไรบทความนี้มีรหัส R สำหรับสร้างชุดข้อมูลของคุณเอง (และคุณสามารถปรับแต่งรหัสเพื่อเลือกสถานะอื่นได้) หากคุณสร้างเวอร์ชันข้อมูลของคุณเองโค้ดสำหรับเอกสาร R Markdown พื้นฐานโดยใช้ไฟล์ข้อมูลแยกต่างหากก็อยู่ใน GitHub เช่นกัน

ไม่ว่าคุณจะเลือกเอกสาร R Markdown แบบใดคุณจะเห็นว่าเป็นเอกสารคงที่ส่วนใหญ่มีการโต้ตอบ แต่ถ้าฉันต้องการให้เอกสารทั้งหมดเป็นแบบโต้ตอบ - ในกรณีนี้โปรดดูการเปลี่ยนแปลงฮิสโตแกรมและข้อความรวมทั้งตาราง ผู้ใช้จะสามารถเลือกแต่ละเมืองและดูข้อมูลทั้งหมดที่กรองเพื่อแสดงเฉพาะสถานที่เหล่านั้นได้อย่างไร

ทางออกหนึ่งคือการสร้างเพจสำหรับแต่ละเมืองซึ่งเป็นไปได้ด้วยสคริปต์ R หากคุณใช้สิ่งที่เรียกว่ารายงานแบบกำหนดพารามิเตอร์ อย่างไรก็ตามคุณยังสามารถสร้างเอกสาร R Markdown เดียวที่ทำหน้าที่เหมือนแอปแบบโต้ตอบได้

เพิ่มการโต้ตอบที่เป็นประกาย

ในการเพิ่มการโต้ตอบแบบ Shiny ให้กับเอกสาร R Markdown ทั่วไปให้เริ่มต้นด้วยการเพิ่มruntime: shinyในส่วนหัว YAML ของเอกสารเช่น:

---

title: "รายได้ครัวเรือนเฉลี่ยตามรหัสไปรษณีย์"

เอาต์พุต: html_document

รันไทม์: มันวาว

---

เมื่อคุณดำเนินการดังกล่าวแล้วกดบันทึกไอคอนถักใน RStudio จะเปลี่ยนเป็น“ เรียกใช้เอกสาร” แม้ว่าผลลัพธ์จะยังคงระบุว่า“ html_document” แต่ก็ไม่ใช่ HTML ธรรมดาอีกต่อไป ตอนนี้เป็นแอปพลิเคชั่น mini-Shiny 

ชารอนมัคลิส / ชารอนมัคลิส

ให้ผู้ใช้เลือกข้อมูล

ตอนนี้ฉันต้องการวิธีให้ผู้ใช้ตัดสินใจเลือกข้อมูล Shiny มี "วิดเจ็ตอินพุต" จำนวนมากสำหรับสิ่งนี้ ฉันจะใช้selectInput()ซึ่งสร้างรายการแบบเลื่อนลงและอนุญาตให้ผู้ใช้เลือกมากกว่าหนึ่งรายการ Shiny มีวิดเจ็ตอื่น ๆ สำหรับปุ่มตัวเลือกอินพุตข้อความตัวเลือกวันที่และอื่น ๆ คุณสามารถดูคอลเล็กชันได้ที่ Shiny Widgets Gallery ของ RStudio 

รหัสสำหรับselectInput()รายการแบบเลื่อนลงของมินิแอปของฉันมีข้อโต้แย้งห้าข้อและมีลักษณะดังนี้:

selectInput ("mycities", "เลือก 1 เมืองหรือมากกว่า:",

ตัวเลือก = เรียงลำดับ (ไม่ซ้ำกัน (markdowndata $ City)),

ที่เลือก = "บอสตัน", multiple = TRUE)

อาร์กิวเมนต์แรก  selectInput(), mycitiesเป็นชื่อตัวแปรที่ผมได้เลือกที่จะเก็บสิ่งที่เห็นคุณค่าของเพื่อนผู้ใช้ อาร์กิวเมนต์ที่สองคือข้อความส่วนหัวที่จะปรากฏพร้อมกับรายการแบบเลื่อนลง อาร์กิวเมนต์ที่สามchoicesเป็นเวกเตอร์ของค่าที่เป็นไปได้ทั้งหมดในรายการแบบเลื่อนลง - ในกรณีนี้ค่าที่ไม่ซ้ำกันของชื่อเมืองในข้อมูลของฉันจะเรียงตามตัวอักษร selected = Bostonหมายความว่าเมนูแบบเลื่อนลงจะเป็นค่าเริ่มต้นที่บอสตันเป็นเมืองที่เลือก และสุดท้ายmultiple = TRUEให้ผู้ใช้เลือกเมืองได้มากกว่าหนึ่งเมือง

รหัสนี้สร้างรายการแบบเลื่อนลง HTML หากคุณเรียกใช้selectInput()โค้ดนั้นในคอนโซล R ของคุณโค้ดจะสร้าง HTML สำหรับดรอปดาวน์ (สมมติว่าคุณโหลด Shiny และเฟรมข้อมูลที่เรียกว่า markdowndata พร้อมคอลัมน์ City) 

ต่อไปฉันต้องเขียน R เพื่อให้ดรอปดาวน์นี้ทำอะไรได้จริง

สร้างตัวแปรแบบไดนามิก

ฉันจะเขียนโค้ดตรรกะการโต้ตอบนี้เป็นสองส่วน:

  1. สร้างกรอบข้อมูลฉันจะเรียกมันmydataว่าซึ่งจะถูกกรองทุกครั้งที่ผู้ใช้เลือกเมือง
  2. เขียนโค้ดสำหรับข้อความฮิสโตแกรมและตารางข้อมูลซึ่งทั้งหมดจะเปลี่ยนแปลงตามกรอบข้อมูลไดนามิกของฉัน

สิ่งสำคัญที่สุดที่ต้องจำไว้ ณ จุดนี้คือวัตถุเหล่านี้ไม่ใช่ตัวแปร R "ปกติ" อีกต่อไป พวกเขากำลังแบบไดนามิก พวกเขาเปลี่ยนแปลงไปตามการกระทำของผู้ใช้ และนั่นหมายความว่ามันทำงานแตกต่างจากตัวแปรที่คุณอาจคุ้นเคยเล็กน้อย

มีอะไรพิเศษเกี่ยวกับพวกเขา? สามสิ่งที่คุณต้องรู้มีดังนี้

  1. ในการเข้าถึงค่าของตัวแปรการป้อนข้อมูลที่จัดเก็บข้อมูลจากผู้ใช้ของคุณคุณต้องไวยากรณ์ไม่เพียงinput$myvarname myvarnameดังนั้นสำหรับค่าที่เก็บไว้ในรายการแบบเลื่อนลงใช้ mycitiesinput$mycities
  2. ออบเจ็กต์เช่นกราฟและตารางที่ขึ้นอยู่กับค่าจากผู้ใช้ของคุณยังเป็นแบบไดนามิกและจำเป็นต้องมีปฏิกิริยา ว่าเป็นเรื่องง่ายเหมือนห่อไว้ในฟังก์ชั่นพิเศษ แต่คุณต้องจำไว้ที่จะทำมัน พวกเขายังไม่สามารถเข้าถึงได้โดยเพียงแค่ชื่อของพวกเขา แต่ต้องวงเล็บเช่นกัน: ไวยากรณ์เหมือนและไม่ได้myvar()myvar
  3. เมื่อคุณ  แสดงเนื้อหาแบบไดนามิกอีกครั้งสิ่งต่างๆเช่นตารางแผนที่ฮิสโตแกรมหรือแม้แต่ข้อความจำเป็นต้องแสดงผลด้วยวิธีพิเศษโดยปกติจะใช้ฟังก์ชันการแสดงผลพิเศษอย่างใดอย่างหนึ่งของ Shiny ข่าวดีก็คือ Shiny ดูแลฟังก์ชันส่วนใหญ่ของการตรวจสอบการเปลี่ยนแปลงและการคำนวณผลลัพธ์ คุณเพียงแค่ต้องรู้ว่าจะใช้ฟังก์ชันใดจากนั้นรวมไว้ในรหัสของคุณ

ทั้งหมดนี้มักจะง่ายกว่าที่อาจฟังดู ต่อไปนี้คือวิธีสร้าง data frame ที่เรียกmydataว่าการเปลี่ยนแปลงทุกครั้งที่ผู้ใช้เลือกเมืองด้วยmycities selectInput()เมนูแบบเลื่อนลง:

mydata <- reactive({

filter(markdowndata, City %in% input$mycities)

})

mydataวัตถุตอนนี้ถือแสดงออกปฏิกิริยา mycitiesและจะเปลี่ยนค่าในแต่ละครั้งที่ผู้ใช้จะทำให้การเปลี่ยนแปลงในรายการแบบเลื่อนลงควบคุม

แสดงตัวแปรแบบไดนามิก

ตอนนี้ฉันต้องการเขียนโค้ดตารางโดยใช้mydataข้อมูลที่กรองแล้ว

อย่างที่คุณอาจเดาได้ตอนนี้ใช้DT::datatable(mydata)ไม่ได้ และมีสองเหตุผลว่าทำไม

ประการแรกเนื่องจากmydataเป็นนิพจน์ปฏิกิริยาคุณจึงไม่สามารถอ้างถึงโดยใช้ชื่อเพียงอย่างเดียว ต้องมีวงเล็บตามหลังเช่น   mydata().

But, second, DT::datatable(mydata()) won’t work as standalone code, either. You’ll get an error message something like this: 

 Operation not allowed without an active reactive context.

(You tried to do something that can only be done from inside

a reactive expression or observer.)

You may see versions of this error message quite often when you’re first starting out. It means that you’re trying to display something dynamic using conventional R syntax.

To fix this, I need a Shiny render function. Several visualization packages have their own special Shiny render functions, including DT. Its render function is renderDT(). If I add renderDT ({  }) around the DT code and run the document again, that should work.

This is my table code:

renderDT({

DT::datatable(mydata(), filter = 'top') %>%

formatCurrency(4:5, digits = 0) %>%

formatCurrency(6, currency = "", digits = 0)

})

Note: In addition to creating and displaying the table, this code also adds some formatting. Columns 4 and 5 show as currency, with a dollar sign and commas. The second formatCurrency() line for column 6 adds the commas to the rounded numbers without a dollar sign, since I specified "" as the currency symbol.

I can use the same mydata() reactive data frame to create a histogram, using another Shiny render function: renderPlot().

renderPlot({

ggplot2::ggplot(mydata(), aes(x = MedianHouseholdIncome)) +

geom_histogram(binwidth = 20000, color = "black", fill = "darkgreen") +

theme_classic() +

xlab("") +

ylab("") +

scale_x_continuous(labels = dollar)

})

That code also includes a little ggplot styling, such as choosing colors for the bar outline and fill and changing the graph's theme. The last line formats the x axis to add dollar signs and commas, and it requires the scales package.

Each one of these blocks of R code needs to be within an R Markdown R code chunk, just like any other R code chunks in a conventional Markdown document. That could look something like the code below, which also names the chunk “histo” (names are optional) and sets the width and height of my plot in inches.

```{r histo, fig.width = 3, fig.height = 2}

renderPlot({

ggplot2::ggplot(mydata(), aes(x = MedianHouseholdIncome)) +

geom_histogram(binwidth = 20000, color = "black", fill = "darkgreen") +

theme_classic() +

xlab("") +

ylab("") +

scale_x_continuous(labels = dollar)

})

```

If I'd like to display interactive text that changes with the user's selection, I need a Shiny render function that's named—surprise!—renderText(). You can put that inside a code chunk, or use alternative R Markdown syntax format outside of code chunks like this:

I have some plain text and then add  `r R CODE WILL BE EVALUATED HERE`

The syntax for this is one backtick followed immediately by a lower-case r, a space, the R code you want evaluated, and ending with another single backtick. So, to add a dynamic headline for the histogram, you could use code like this:

Histogram for `r renderText({input$mycities})`

This works fine for a single city. However, if there's more than one city, that code will just display the names without commas between them, such as Boston Cambridge Amherst. For public-facing code,  you'd want to pretty that up a bit, perhaps using base R's paste() function:

Histogram for `r renderText({paste(input$mycities,

sep = " ", collapse = ", ")})`

You can use similar code to generate text that tells users the ZIP codes with highest and lowest median incomes. For those calculations, I created one reactive data frame containing the row with the highest household income and another with the lowest.

I also discovered that the lowest median income was being suspiciously low—$2,500 in the college-town community of Amherst, Mass.—where the median monthly housing cost is $1,215. My guess is that's a concentration of student housing, so I excluded any ZIP code with median household income of less than $5,000.

Here is code to create those two data frames:

zip_highest_income_row <- reactive({

filter(mydata(), MedianHouseholdIncome == max(MedianHouseholdIncome, na.rm = TRUE))

})

zip_lowest_income_row <- reactive({

filter(mydata(), MedianHouseholdIncome >= 5000) %>%

filter(MedianHouseholdIncome == min(MedianHouseholdIncome, na.rm = TRUE))

})

This should look like typical dplyr filter() code, except that 1) each is wrapped in a reactive({ }) function, and 2) the mydata dynamic data frame which changes based on user input is referred to as mydata() and not simply mydata

To show the value of the first item in the zip_highest_income_row data frame's ZIP column, I can't use usual R code like zip_highest_income_row$Zip[1]. Instead,  I need to refer to the dynamic data frame with parentheses: zip_highest_income_row()$Zip[1] . And then wrap that in a Shiny render() function—in this case renderText():

ZIP code `r renderText(zip_highest_income_row()$ZipCode[1])` in

`r renderText(zip_highest_income_row()$City[1])`

has the highest median income in the place(s) you selected,

`r renderText(scales::dollar(zip_highest_income_row()$MedianHouseholdIncome[1]))`.

ZIP code `r renderText(zip_lowest_income_row()$ZipCode[1])` in

`r renderText(zip_lowest_income_row()$City[1])` has the lowest

median income in the place(s) you selected,

`r renderText(scales::dollar(zip_lowest_income_row()$MedianHouseholdIncome[1]))`.

Run and share your Shiny app

Once you add runtime: shiny to an R Markdown, it’s not an HTML file anymore—it’s a mini Shiny application. And that means it needs a Shiny server to run.

As I mentioned earlier, anyone with R, RStudio, and the shiny package has a Shiny server on their local system. That makes it easy to share any Shiny app with fellow R users. You can send them a document by email or, more elegantly, post it online as a zipped file and use the shiny::runUrl() command. There are special runGitHub() and runGist() functions for apps on GitHub that are convenient if you use GitHub for projects, which will automatically zip additional files in your project, such as data files.

But chances are, at some point you’ll want to show your work to non-R users, and that requires a publicly accessibly Shiny server. Probably the easiest option is RStudio’s shinyapps.io service. It’s free for a few limited public apps with very light usage. Paid accounts are priced based on the number of active hours they offer for your apps. Active hours measure time the application is actively being used—one person on for an hour is the same hour as 100 people in that hour. To ensure 24x7 uptime for a couple of apps, you’d need the $1,100/year standard account with 2,000 hours.

You can also build your own Shiny server on a cloud service like AWS and installations for R and the free version of RStudio’s Shiny server software. There’s a great step-by-step tutorial by Dean Attali showing how to do so at Digital Ocean, where you can build and run a small Shiny server for just $5 per month of hosting costs without worrying about active hours. The trade-off is doing your own patching and R/library updates—and you may need a heftier virtual server than the cheapest 1G droplet for robust applications.

Add an interactive map

Finally, I'll walk you through how I added an interactive map to this document using the leaflet package.

First, you need a file with geospatial data as well as numerical data, so your app knows the shape of each ZIP code. The code below explains how create a spatial data frame using the tidycensus and sf packages.

For interactivity, I'll create a dynamic version of that spatial data, so only the selected cities show up on the map. Below is my code for doing that. It may look a little repetitive, but I'm going for readability instead of brevity. Feel free to tighten your own version.

mapdata <- reactive({

if("All Mass" %in% input$mycities){

ma_appdata_for_map %>%

dplyr::select(ZipCode = GEOID, MedianHouseholdIncome = medincome, MedianMonthlyHousingCost = medmonthlyhousingcost, Population = pop, City, County = county.name, State, Lat, Long, income, housing, Pop, geometry) %>%

mutate(

Highlighted = "Yes"

) %>%

sf::st_as_sf()

} else {

dplyr::filter(ma_appdata_for_map, City %in% input$mycities) %>%

dplyr::select(ZipCode = GEOID, MedianHouseholdIncome = medincome, MedianMonthlyHousingCost = medmonthlyhousingcost, Population = pop, City, County = county.name, State, Lat, Long, income, housing, Pop, geometry) %>%

dplyr::mutate(

Highlighted = ifelse(City %in% input$mycities, "Yes", "No")

) %>%

sf::st_as_sf()

}

})

The reactive function should be familiar by now. My if and else statements take into account whether the user has chosen All Mass or just individual cities. For any choice but All Mass, I filter for just the selected cities. In both cases I'm using a conventional dplyr select() function to choose which columns I want in the map, making sure to include Lat for latitude, Long for longitude, and geometry that holds the ZIP code polygon shape files. The last line in each if() code section makes sure the results are an sf (simple features) geospatial object. While I didn't need that code on my local Mac, the app worked better on shinyapps.io when I included it.

Now it’s time to work on map colors. I'll set up two reactive color palettes for my leaflet map, one for income and the other for housing costs. In both cases I use greens, but you could choose any you'd like. 

incomepal <- reactive({

leaflet::colorNumeric(palette = "Greens",

domain = mapdata()$MedianHouseholdIncome)

})

housingpal <- reactive({

leaflet::colorNumeric(palette = "Greens",

domain = mapdata()$MedianMonthlyHousingCost)

})

I want these to be reactive, too, so they change based on user selections. The domain argument defines the values that the palette will be displaying. In the first case, it’s my reactive mapdata object's MedianHouseholdIncome column—with mapdata coded as mapdata() since it's reactive; in the second case, it's the MedianMonthlyHousingCost column.

I'll also set up exactly how I want my popup text to appear. This can take a mixture of HTML (the

is an HTML line break) and data frame columns. While you can certainly use base R’s paste() or paste0() functions, I find the glue package much easier when dealing with more than one variable mixed in with text. You can see below that I just need to enclose variables I want evaluated within curly braces. Of course, the popup text needs to be reactive as well, so it, too, changes with the user’s selection.

mypopups <- reactive({

glue::glue("ZIP Code: {mapdata()$ZipCode}

Median Household Income: {mapdata()$income}

Median Monthly Housing Cost: {mapdata()$housing}

Population: {mapdata()$Pop}

City: {mapdata()$City}

County: {mapdata()$County}")

})

Finally, code for the leaflet map itself.

leaflet::renderLeaflet({

leaflet(mapdata()) %>%

addProviderTiles("CartoDB.Positron") %>%

addPolygons(fillColor = ~incomepal()(mapdata()$MedianHouseholdIncome),

fillOpacity = 0.7,

weight = 1.0,

color = "black",

smoothFactor = 0.2,

popup = mypopups(),

group = "Household Income"

) %>%

addPolygons(fillColor = ~housingpal()(mapdata()$MedianMonthlyHousingCost),

fillOpacity = 0.7,

weight = 0.2,

color = "black",

smoothFactor = 0.2,

popup = mypopups(),

group = "Housing Costs"

) %>%

addLayersControl(

baseGroups=c("Household Income", "Housing Costs"),

position = "bottomleft",

options = layersControlOptions(collapsed = FALSE)

)

})

renderLeaflet() is the Shiny render function that will display the dynamic dataviz relying on the dynamic mapdata object. Inside that function is "regular" leaflet mapping code. The first line, leaflet(mapdata()), creates an R leaflet object from the reactive mapdata object. It is using the leaflet package, which is an R wrapper to the leaflet.js library. Next line adds a style of background map tiles from CartoDB.

The addPolygons() function tells leaflet how to display the ZIP code polygons. I want it colored by the MideanHouseholdIncome column using the income palette I set up earlier, incomepal. Most of the rest of those arguments are styling. The popup argument sets the popup text to be the mypopups object I created earlier, and the group argument gives a name to the map layer.

ฉันเพิ่มเลเยอร์อื่นที่คล้ายกันสำหรับค่าที่พักเฉลี่ยรายเดือน และในที่สุดก็addLayersControl()วางตำนานที่คลิกได้สำหรับแต่ละเลเยอร์ที่ด้านล่างซ้าย

ชารอนมัคลิส /

หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับการทำแผนที่ใน R ด้วยแผ่นพับโปรดดูบทช่วยสอนของฉัน "สร้างแผนที่ใน R ในขั้นตอนง่ายๆ 10 ขั้นตอน (ค่อนข้าง)"

ไฟล์ R markdown สุดท้าย

คุณสามารถดูไฟล์ R Markdown สุดท้ายบน GitHub หากคุณดูโค้ดอย่างละเอียดคุณอาจสังเกตเห็นส่วนเพิ่มเติมบางส่วน ฉันเพิ่ม All Mass selectInput()ลงในเวกเตอร์ตัวเลือกรายการแบบเลื่อนลงดังนั้นรหัสจึงเป็นตอนนี้

selectInput ("mycities", "เลือก 1 เมืองหรือมากกว่า:",

choice = c ("All Mass", sort (unique (markdowndata $ City))),

multi = TRUE, selected = "บอสตัน")

And then I tweaked several other lines of code to give a different option if All Mass is selected, such as creating a dynamic variable selected_places that will say "Massachusetts" if "All Mass" is one of the selected cities.

selected_places <- reactive({

if("All Mass" %in% input$mycities){

"Massachusetts"

} else {

paste(input$mycities,

sep = " ", collapse = ", ")

}

})

Note also the new YAML header:

---

title: "Median Household Income by ZIP Code"

output: html_document

resource_files:

- mamarkdowndata.rdata

- zip_mass_appdata_for_map.rds

runtime: shiny

---

That resources_files: option says that this document requires two other files in order to run, mamarkdowndata.rdata and zip_mass_appdata_for_map.rds. This lets shinyapps.io know those files need to be uploaded along with the main R Markdown document when deploying a file with  rsconnect::deployDoc("docname.Rmd").

You can see this interactive R Markdown document with Shiny in action at //idgrapps.shinyapps.io/runtimeshiny/. It may take a little while to load, since I didn't attempt to optimize this code for speed. RStudio has some resources if you want to learn about speeding up Shiny apps.

How is this different from a 'real' Shiny app?

This super-charged-with-Shiny R Markdown document differs from a full-fledged Shiny app in a few key ways.

1. A Shiny app needs to be in one file called app.R or two files ui.R and server.R. The app can source additional files with other names, but that file-naming structure is absolute. In a one-file app.R app, sections are needed for the ui (user interface, which defines what the user sees and interacts with) and the server.

2. Shiny app layouts are built around the Bootstrap page grid framework. You can see more about layout structure at RStudio's Shiny application layout guide.

3. Most dynamic components that you want to render, including things like graphs and tables, need to be specifically placed somewhere on the page with additional Output functions and definitions. For example, an interactive leaflet map would need code such as leafletOutput("mymap") somewhere in the ui to tell the app where it should display,  in addition to server code such as 

output$mymap <- renderLeaflet({ #MAP CODE HERE }) 

to define the logic behind generating the map.

Here is an example of a Shiny app.R file for this app's histogram and table:

library("shiny")

library("dplyr")

library("ggplot2")

library("DT")

options(scipen = 999)

load("mamarkdowndata.rdata") # loads variable markdowndata

ma_appdata_for_map <- readRDS("zip_mass_appdata_for_map.rds")

# Define UI

ui <- fluidPage(

# Application title

titlePanel("Income and Housing Costs by ZIP Code"),

# Sidebar

sidebarLayout(

sidebarPanel(

selectInput("mycities", "Choose 1 or more Massachusetts places: ", choices = c("All Mass", sort(unique(markdowndata$City))), multiple = TRUE, selected = "Boston"),

br(),

strong("Note: some cities may have more than one place name for ZIP codes. For example, Allston, Brighton, Dorchester, and several other neighborhoods are not included in ZIP code place name \"Boston\".")

),

# Show histogram

mainPanel(

h4(htmlOutput("histogramHeadline")),

plotOutput("myhistogram"),

br(),

h4(htmlOutput("tableHeadline")),

DTOutput("mytable")

)

)

)

# Define server logic required to draw a histogram

server <- function(input, output) {

mydata <- reactive({

if("All Mass" %in% input$mycities){

markdowndata

} else {

filter(markdowndata, City %in% input$mycities)

}

})

selected_places <- reactive({

if("All Mass" %in% input$mycities){

"Massachusetts"

} else {

paste(input$mycities,

sep = " ", collapse = ", ")

}

})

output$histogramHeadline <- renderUI({

paste("Histogram for", selected_places(), " income data")

})

output$tableHeadline <- renderUI({

paste("Data for", selected_places())

})

output$myhistogram <- renderPlot({

ggplot(mydata(), aes(x = MedianHouseholdIncome)) +

geom_histogram(binwidth = 20000, color = "black", fill = "darkgreen") +

theme_classic() +

xlab("") +

ylab("") +

scale_x_continuous(labels = dollar)

})

output$mytable <- renderDT({

DT::datatable(mydata(), filter = 'top') %>%

formatCurrency(4:5, digits = 0) %>%

formatCurrency (6, สกุลเงิน = "", หลัก = 0)

})

}

# เรียกใช้แอปพลิเคชัน

shinyApp (ui = ui, เซิร์ฟเวอร์ = เซิร์ฟเวอร์)

คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับการสร้างแอป Shiny ประเภทนี้ได้ที่บทแนะนำ Shiny ของ RStudio

สำหรับเคล็ดลับ R เพิ่มเติมให้ไปที่หน้าวิดีโอ Do More With R หรือรายการเล่น Do More With R บน YouTube