Chuyển đến nội dung chính

Trực quan hóa dữ liệu với Highcharter trong R

Tìm hiểu cách dùng Highcharter để tạo trực quan hóa gây ấn tượng.
· 12 phút đọc

Khám phá cùng AI

Tôi cho rằng trực quan hóa dữ liệu là kỹ thuật tốt nhất để trình bày mọi báo cáo mô tả và phân tích trên một tập dữ liệu. Tôi là kiểu người rất yêu thích trực quan hóa dữ liệu. Bạn có thể kể trọn câu chuyện trên một màn hình duy nhất — tất nhiên còn tùy vào độ phức tạp của dữ liệu. Nếu bạn đang đọc hướng dẫn này, chắc hẳn bạn đã biết đến gói Ggplot2 trong R dùng để tạo ra những biểu đồ tuyệt vời cho phân tích nhưng phần nào thiếu tính năng động.

Quay lại với Highcharter: đây là một wrapper trong R của thư viện javascript HighCharts và các mô-đun của nó.

Các tính năng chính của gói này gồm:

  • Bạn có thể tạo nhiều loại biểu đồ với cùng một phong cách như scatter, bubble, chuỗi thời gian, heatmap, treemap, biểu đồ thanh, v.v.
  • Hỗ trợ nhiều đối tượng R.
  • Hỗ trợ Highstocks Charts, Choropleths.
  • Có phong cách piping được mọi người dùng và lập trình viên R yêu thích.
  • Nhiều chủ đề với diện mạo bắt mắt.

Hãy bắt tay vào thực hành và tạo một vài trực quan hóa với Highcharter theo các tính năng nêu trên:

Tạo một số biểu đồ cơ bản bằng hàm hchart

hchart là một hàm tổng quát nhận một đối tượng và trả về một đối tượng highcharter. Có những hàm có hành vi tương tự các hàm trong gói ggplot2 như:

  • hchart hoạt động giống qplot của ggplot2.
  • hc_add_series hoạt động giống geom_S của ggplot2.
  • hcaes hoạt động giống aes của ggplot2.

Hãy chọn một bộ dữ liệu. Tôi sẽ lấy bộ dữ liệu Pokemon cũng được cung cấp trong gói Highcharter. Xem lướt qua bộ dữ liệu.

glimpse(pokemon)
## Observations: 718
## Variables: 20
## $ id              <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,...
## $ pokemon         <chr> "bulbasaur", "ivysaur", "venusaur", "charmande...
## $ species_id      <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,...
## $ height          <int> 7, 10, 20, 6, 11, 17, 5, 10, 16, 3, 7, 11, 3, ...
## $ weight          <int> 69, 130, 1000, 85, 190, 905, 90, 225, 855, 29,...
## $ base_experience <int> 64, 142, 236, 62, 142, 240, 63, 142, 239, 39, ...
## $ type_1          <chr> "grass", "grass", "grass", "fire", "fire", "fi...
## $ type_2          <chr> "poison", "poison", "poison", NA, NA, "flying"...
## $ attack          <int> 49, 62, 82, 52, 64, 84, 48, 63, 83, 30, 20, 45...
## $ defense         <int> 49, 63, 83, 43, 58, 78, 65, 80, 100, 35, 55, 5...
## $ hp              <int> 45, 60, 80, 39, 58, 78, 44, 59, 79, 45, 50, 60...
## $ special_attack  <int> 65, 80, 100, 60, 80, 109, 50, 65, 85, 20, 25, ...
## $ special_defense <int> 65, 80, 100, 50, 65, 85, 64, 80, 105, 20, 25, ...
## $ speed           <int> 45, 60, 80, 65, 80, 100, 43, 58, 78, 45, 30, 7...
## $ color_1         <chr> "#78C850", "#78C850", "#78C850", "#F08030", "#...
## $ color_2         <chr> "#A040A0", "#A040A0", "#A040A0", NA, NA, "#A89...
## $ color_f         <chr> "#81A763", "#81A763", "#81A763", "#F08030", "#...
## $ egg_group_1     <chr> "monster", "monster", "monster", "monster", "m...
## $ egg_group_2     <chr> "plant", "plant", "plant", "dragon", "dragon",...
## $ url_image       <chr> "1.png", "2.png", "3.png", "4.png", "5.png", "...

Hãy vẽ một biểu đồ thanh.

pokemon%>%
  count(type_1)%>%
  arrange(n)%>%
  hchart(type = "bar", hcaes(x = type_1, y = n))
bar chart

Vậy là bạn đã có một biểu đồ thanh về hạng mục type 1 của pokemon.

Giả sử bạn muốn biểu đồ cột thay thế thì biến duy nhất cần đổi là type thành column.

pokemon%>%
  count(type_1)%>%
  arrange(n)%>%
  hchart(type = "column", hcaes(x = type_1, y = n))
bar chart 2

Treemap

pokemon%>%
  count(type_1)%>%
  arrange(n)%>%
  hchart(type = "treemap", hcaes(x = type_1, value = n, color = n))
treemap

Chúng ta cũng có thể dùng hc_add_series để vẽ biểu đồ. Nó được dùng để thêm và xóa chuỗi khỏi đối tượng highchart.

scatterplot

Biểu đồ phân tán

highchart()%>%
  hc_add_series(pokemon, "scatter", hcaes(x = height, y = weight))

Khác biệt chính giữa các hàm geom_ của ggplot2 và hc_add_series là chúng ta cần thêm dữ liệu và thẩm mỹ (aesthetics) một cách tường minh ở mỗi hàm, trong khi với ggplot2, có thể thêm dữ liệu và thẩm mỹ ở một lớp rồi tiếp tục thêm nhiều geom khác dùng chung dữ liệu và thẩm mỹ đó.

Một ví dụ chính xác được đưa ra dưới đây sử dụng bộ dữ liệu diamond trong gói ggplot2.

data(diamonds, package = "ggplot2")

set.seed(123)
data <- sample_n(diamonds, 300)

modlss <- loess(price ~ carat, data = data)
fit <- arrange(augment(modlss), carat)

highchart() %>%
  hc_add_series(data, type = "scatter",
                hcaes(x = carat, y = price, size = depth, group = cut)) %>%
  hc_add_series(fit, type = "line", hcaes(x = carat, y = .fitted),
                name = "Fit", id = "fit") %>%
  hc_add_series(fit, type = "arearange",
                hcaes(x = carat, low = .fitted - 2*.se.fit,
                      high = .fitted + 2*.se.fit),
                linkedTo = "fit")
scatterplot 2

Như trong ví dụ, biểu đồ được vẽ bằng ba chuỗi gồm phân tán, đường và dải diện tích.

Hãy tái hiện một phát triển javascript của highchart vào R bằng hc_add_series.

highchart's javascript

highchart() %>%
  hc_chart(type = "area") %>%
  hc_title(text = "Historic and Estimated Worldwide Population Distribution by Region") %>%
  hc_subtitle(text = "Source: Wikipedia.org") %>%
  hc_xAxis(categories = c("1750", "1800", "1850", "1900", "1950", "1999", "2050"),
           tickmarkPlacement = "on",
           title = list(enabled = FALSE)) %>%
  hc_yAxis(title = list(text = "Percent")) %>%
  hc_tooltip(pointFormat = "<span style=\"color:{series.color}\">{series.name}</span>:
             <b>{point.percentage:.1f}%</b> ({point.y:,.0f} millions)<br/>",
             shared = TRUE) %>%
  hc_plotOptions(area = list(
     stacking = "percent",
     lineColor = "#ffffff",
     lineWidth = 1,
     marker = list(
       lineWidth = 1,
       lineColor = "#ffffff"
       ))
     ) %>%
  hc_add_series(name = "Asia", data = c(502, 635, 809, 947, 1402, 3634, 5268)) %>%
  hc_add_series(name = "Africa", data = c(106, 107, 111, 133, 221, 767, 1766)) %>%
  hc_add_series(name = "Europe", data = c(163, 203, 276, 408, 547, 729, 628)) %>%
  hc_add_series(name = "America", data = c(18, 31, 54, 156, 339, 818, 1201)) %>%
  hc_add_series(name = "Oceania", data = c(2, 2, 2, 6, 13, 30, 46))
linechart

Khi so sánh, bạn sẽ thấy từng khối mã javascript được chuyển thành các hàm R nối với nhau bằng pipeline. Chúng ta có thể xem qua một vài đối số như pointFormat của hctooltip với định dạng giống hệt trong mã Javascript; bạn có thể xem chi tiết tại đây.

Highstocks

Highstocks là các biểu đồ cho phân tích tài chính và chuỗi thời gian, hoạt động tốt với thư viện quamtmod, và rất dễ để vẽ các mã chứng khoán, sau đó bạn có thể thêm nhiều chuỗi bằng hc_add_series.

x <- getSymbols("GOOG", auto.assign = FALSE)

hchart(x)
highstocks

Như trên biểu đồ, chúng ta không cần thêm mã bổ sung, hchart làm việc rất hiệu quả với đối tượng xts và cung cấp ảnh chụp động của dữ liệu. Bạn có thể dùng chức năng thu phóng để đi sâu dữ liệu thành các phần nhỏ hơn nhằm phân tích tốt hơn.

Hãy dùng hc_add_series.

y <- getSymbols("AMZN", auto.assign = FALSE)

highchart(type = "stock") %>%
  hc_add_series(x) %>%
  hc_add_series(y, type = "ohlc")
highstocks 2

Như bạn thấy, trực quan hóa điều chỉnh lượng dữ liệu lớn rất hiệu quả.

Hãy thử stock với các đối tượng xts khác nhau.

Highmaps

Cách dễ nhất để vẽ bản đồ với highcharter là dùng hàm hcmap. Chọn một URL từ bộ sưu tập highmaps và dùng URL đó làm bản đồ trong hàm hcmap. Điều này sẽ tải xuống bản đồ và tạo một đối tượng dùng thông tin đó làm đối số mapdata.

Hãy vẽ bản đồ Ấn Độ.

hcmap("https://code.highcharts.com/mapdata/countries/in/in-all.js")%>%
  hc_title(text = "India")
highmap

Đó là một bản đồ đơn giản, vậy còn chuyển nó thành choropleth thì sao.

Mỗi dữ liệu bản đồ tải về từ bộ sưu tập bản đồ highcharts đều có khóa để nối dữ liệu. Có 2 hàm hỗ trợ để biết các vùng được mã hóa nhằm nối bản đồ và dữ liệu:

  • download_map_data: Tải dữ liệu geojson từ bộ sưu tập highcharts.
  • get_data_from_map: Lấy thuộc tính cho từng vùng trên bản đồ, như các khóa từ dữ liệu bản đồ.
mapdata <- get_data_from_map(download_map_data("https://code.highcharts.com/mapdata/countries/in/in-all.js"))

glimpse(mapdata)
## Observations: 34
## Variables: 20
## $ `hc-group`    <chr> "admin1", "admin1", "admin1", "admin1", "admin1"...
## $ `hc-middle-x` <dbl> 0.65, 0.59, 0.50, 0.56, 0.46, 0.46, 0.51, 0.59, ...
## $ `hc-middle-y` <dbl> 0.81, 0.63, 0.74, 0.38, 0.64, 0.51, 0.34, 0.41, ...
## $ `hc-key`      <chr> "in-py", "in-ld", "in-wb", "in-or", "in-br", "in...
## $ `hc-a2`       <chr> "PY", "LD", "WB", "OR", "BR", "SK", "CT", "TN", ...
## $ labelrank     <chr> "2", "2", "2", "2", "2", "2", "2", "2", "2", "2"...
## $ hasc          <chr> "IN.PY", "IN.LD", "IN.WB", "IN.OR", "IN.BR", "IN...
## $ `alt-name`    <chr> "Pondicherry|Puduchcheri|Pondichéry", "Ã\u008dl...
## $ `woe-id`      <chr> "20070459", "2345748", "2345761", "2345755", "23...
## $ fips          <chr> "IN22", "IN14", "IN28", "IN21", "IN34", "IN29", ...
## $ `postal-code` <chr> "PY", "LD", "WB", "OR", "BR", "SK", "CT", "TN", ...
## $ name          <chr> "Puducherry", "Lakshadweep", "West Bengal", "Ori...
## $ country       <chr> "India", "India", "India", "India", "India", "In...
## $ `type-en`     <chr> "Union Territory", "Union Territory", "State", "...
## $ region        <chr> "South", "South", "East", "East", "East", "East"...
## $ longitude     <chr> "79.7758", "72.7811", "87.7289", "84.4341", "85....
## $ `woe-name`    <chr> "Puducherry", "Lakshadweep", "West Bengal", "Ori...
## $ latitude      <chr> "10.9224", "11.2249", "23.0523", "20.625", "25.6...
## $ `woe-label`   <chr> "Puducherry, IN, India", "Lakshadweep, IN, India...
## $ type          <chr> "Union Territor", "Union Territor", "State", "St...
#population state wise
pop = as.data.frame(c(84673556, 1382611, 31169272, 103804637, 1055450, 25540196, 342853, 242911, 18980000, 1457723, 60383628, 25353081, 6864602,
12548926, 32966238, 61130704, 33387677, 64429, 72597565, 112372972, 2721756, 2964007, 1091014, 1980602, 41947358, 1244464,
27704236, 68621012, 607688, 72138958, 3671032, 207281477, 10116752,91347736))

state= mapdata%>%
  select(`hc-a2`)%>%
  arrange(`hc-a2`)

State_pop = as.data.frame(c(state, pop))
names(State_pop)= c("State", "Population")

hcmap("https://code.highcharts.com/mapdata/countries/in/in-all.js", data = State_pop, value = "Population",
      joinBy = c("hc-a2", "State"), name = "Fake data",
      dataLabels = list(enabled = TRUE, format = '{point.name}'),
      borderColor = "#FAFAFA", borderWidth = 0.1,
      tooltip = list(valueDecimals = 0))
highmap 2

Hãy thử nghiệm với hc_add_series trong bản đồ và choropleth. Để biết thêm chi tiết và ví dụ, truy cập tại đây.

PlugIns

Bây giờ hãy thử một số plugin do highcharter cung cấp như nhóm dữ liệu, drill-down, tải xuống, in dữ liệu và một số chủ đề đẹp mắt.

Hãy nhóm dữ liệu của bộ mpg; ở đây, để trực quan hơn, chúng ta tạo một danh sách phân loại dữ liệu theo nhà sản xuất.

data(mpg, package = "ggplot2")

mpgg <- mpg %>%
  filter(class %in% c("suv", "compact", "midsize")) %>%
  group_by(class, manufacturer) %>%
  summarize(count = n())

categories_grouped <- mpgg %>%
  group_by(name = class) %>%
  do(categories = .$manufacturer) %>%
  list_parse()

highchart() %>%
  hc_xAxis(categories = categories_grouped) %>%
  hc_add_series(data = mpgg, type = "bar", hcaes(y = count, color = manufacturer),
                showInLegend = FALSE)
chart

Hãy tạo một biểu đồ có thể drill-down sang biểu đồ khác để phân tích tốt hơn. Bạn có thể hiểu mã hiệu quả hơn nếu nắm vững list trong R.

df <- data_frame(
  name = c("Animals", "Fruits", "Cars"),
  y = c(5, 2, 4),
  drilldown = tolower(name)
)


ds <- list_parse(df)
names(ds) <- NULL


hc <- highchart() %>%
  hc_chart(type = "column") %>%
  hc_title(text = "Basic drilldown") %>%
  hc_xAxis(type = "category") %>%
  hc_legend(enabled = FALSE) %>%
  hc_plotOptions(
    series = list(
      boderWidth = 0,
      dataLabels = list(enabled = TRUE)
    )
  ) %>%
  hc_add_series(
    name = "Things",
    colorByPoint = TRUE,
    data = ds
  )

dfan <- data_frame(
  name = c("Cats", "Dogs", "Cows", "Sheep", "Pigs"),
  value = c(4, 3, 1, 2, 1)
)

dffru <- data_frame(
  name = c("Apple", "Organes"),
  value = c(4, 2)
)

dfcar <- data_frame(
  name = c("Toyota", "Opel", "Volkswage"),
  value = c(4, 2, 2)
)

second_el_to_numeric <- function(ls){

  map(ls, function(x){
    x[[2]] <- as.numeric(x[[2]])
    x
  })

}

dsan <- second_el_to_numeric(list_parse2(dfan))

dsfru <- second_el_to_numeric(list_parse2(dffru))

dscar <- second_el_to_numeric(list_parse2(dfcar))

hc %>%
  hc_drilldown(
    allowPointDrilldown = TRUE,
    series = list(
      list(
        id = "animals",
        data = dsan
      ),
      list(
        id = "fruits",
        data = dsfru
      ),
      list(
        id = "cars",
        data = dscar
      )
    )
  )
chart 2

Hãy áp dụng Drill down trên các biểu đồ khác.

tm <- pokemon %>%
  mutate(type_2 = ifelse(is.na(type_2), paste("only", type_1), type_2),
         type_1 = type_1) %>%
  group_by(type_1, type_2) %>%
  summarise(n = n()) %>%
  ungroup() %>%
  treemap::treemap(index = c("type_1", "type_2"),
                   vSize = "n", vColor = "type_1")
.chart 3
tm$tm <- tm$tm %>%
  tbl_df() %>%
  left_join(pokemon %>% select(type_1, type_2, color_f) %>% distinct(), by = c("type_1", "type_2")) %>%
  left_join(pokemon %>% select(type_1, color_1) %>% distinct(), by = c("type_1")) %>%
  mutate(type_1 = paste0("Main ", type_1),
         color = ifelse(is.na(color_f), color_1, color_f))

highchart() %>%
  hc_add_series_treemap(tm, allowDrillToNode = TRUE,
                        layoutAlgorithm = "squarified")
chart 4

Hãy thêm chức năng xuất dữ liệu.

pokemon%>%
  count(type_1)%>%
  arrange(n)%>%
  hchart(type = "bar", hcaes(x = type_1, y = n, color = type_1))%>%
  hc_exporting(enabled = TRUE)
chart 5 Cuối cùng, chúng ta thêm một vài chủ đề.
pokemon%>%
  count(type_1)%>%
  arrange(n)%>%
  hchart(type = "bar", hcaes(x = type_1, y = n, color = type_1))%>%
  hc_exporting(enabled = TRUE)%>%
hc_add_theme(hc_theme_chalk())
chart 6

Bạn có thể học được rất nhiều từ đây, đây là một thư viện đầy đủ cho Highcharter. Tôi cũng chia sẻ một trong những biểu đồ yêu thích được tạo trên bộ dữ liệu thời tiết; ở đây đối số thú vị polar được dùng: khi đặt TRUE, toàn bộ câu chuyện thay đổi.

data("weather")

x <- c("Min", "Mean", "Max")
y <- sprintf("{point.%s}", c("min_temperaturec", "mean_temperaturec", "max_temperaturec"))
tltip <- tooltip_table(x, y)

hchart(weather, type = "columnrange",
       hcaes(x = date, low = min_temperaturec, high = max_temperaturec,
             color = mean_temperaturec)) %>%
  hc_chart(polar = TRUE) %>%
  hc_yAxis( max = 30, min = -10, labels = list(format = "{value} C"),
            showFirstLabel = FALSE) %>%
  hc_xAxis(
    title = list(text = ""), gridLineWidth = 0.5,
    labels = list(format = "{value: %b}")) %>%
  hc_tooltip(useHTML = TRUE, pointFormat = tltip,
             headerFormat = as.character(tags$small("{point.x:%d %B, %Y}")))
chart 7

Nếu bạn muốn học thêm về Trực quan hóa dữ liệu trong R, hãy tham gia khóa Data Visualization with ggplot2 (Phần 1) của DataCamp và xem Hướng dẫn công thức R của chúng tôi.

Chủ đề

Liên quan