데이터 시각화 어디까지 보고 오셨어요?
한국 R 사용자회
2024년 6월 25일
데이터 시각화 (이광춘, 2018)
데이터 시각화 + 저널리즘
데이터 시각화 + 저널리즘 + 챗GPT
ggplot
데이터 시각화
질의응답!
2023년 챗GPT 데이터 사이언스
2024년 인공지능 데이터 사이언스
gm_highlight <- highlight_key(gapminder,
~country)
life_g <- gm_highlight %>%
ggplot(aes(
x=year,
y=lifeExp,
group=country,
text=paste0("대륙: ", continent, "\n",
"국가: ", country))) +
geom_line()
life_gg <- ggplotly(life_g, tooltip = "text")
highlight(life_gg, on = "plotly_click",
selectize = TRUE,
dynamic = TRUE,
persistent = TRUE)
gapminder_cjk <- gapminder %>%
filter(country %in% c("China", "Japan",
"Korea, Rep."))
gapminder_sd <- SharedData$new(gapminder_cjk,
~country)
life_g <- gapminder_sd %>%
ggplot(aes(
x = year,
y = lifeExp,
group = country,
text = paste0("대륙: ", continent, "\n",
"국가: ", country))) +
geom_line() +
geom_point() +
facet_wrap(~country)
ggplotly(life_g, tooltip = "text")
library(crosstalk); library(DT)
### 1. 공유 데이터
general_sd_df <- general_df %>% select(삼국, 무장, 무력, 지력, 통솔, 생년)
sam_sd <- SharedData$new(general_sd_df)
### 2. 제어
sam_ctrl <- filter_checkbox("삼국", "위촉오", sam_sd, ~삼국, inline = TRUE)
year_ctrl <- filter_slider("생년", "출생년도", sam_sd, ~생년, width = "50%")
### 3. 표
sam_tbl <- sam_sd |>
datatable( extensions="Scroller", style="bootstrap", class="compact", width="100%",
options=list(deferRender=TRUE, scrollY=300, scroller=TRUE))
### 4. 시각화
force_g <- ggplot(sam_sd, aes(x=무력, y=통솔, color=삼국,
text = paste('이름 :', 무장, "\n",
'무력:', 무력, "\n",
'지력:', 지력, "\n",
'통솔:', 통솔, "\n"))) +
geom_point() +
theme(legend.position = "none") +
labs(x="무력", y="통솔") +
scale_color_manual(values = c("red", "green", "blue"))
force_gg <- ggplotly(force_g, tooltip = "text")
### 5. 인터랙티브 시각화
bscols(widths = c(2, 5, 5),
list(year_ctrl, sam_ctrl),
sam_tbl, force_gg)
library(tidyverse)
library(gapminder)
library(plotly)
life_g <- gapminder %>%
ggplot(aes(
x = gdpPercap,
y = lifeExp,
group = country, color=country,
text=paste0("대륙: ", continent, "\n",
"국가: ", country))) +
geom_point(alpha = 0.2) +
geom_point() +
facet_wrap(~continent, ncol = 2) +
scale_x_sqrt() +
theme(legend.position = "none")
ggplotly(life_g, tooltip="text")
frame= 매개변수
만 넣으면 됨.
library(tidyverse)
library(gapminder)
library(plotly)
life_g <- gapminder %>%
ggplot(aes(
x = gdpPercap,
y = lifeExp,
group = country, color=country,
text=paste0("대륙: ", continent, "\n",
"국가: ", country))) +
geom_point(alpha = 0.2) +
geom_point(aes(frame = year), color="red") +
facet_wrap(~continent, ncol = 2) +
scale_x_sqrt() +
theme(legend.position = "none")
ggplotly(life_g, tooltip="text")
crosstalk
library(crosstalk); library(plotly); library(leaflet); library(palmerpenguins)
penguins <- na.omit(penguins) # Remove missing data for simplicity
penguins$longitude <- rnorm(nrow(penguins), mean = -64, sd = 0.1)
penguins$latitude <- rnorm(nrow(penguins), mean = -64, sd = 0.1)
# Create a shared data object
sd_penguins <- SharedData$new(penguins)
# Plot with plotly
penguin_plot <- plot_ly(sd_penguins, x = ~bill_length_mm, y = ~flipper_length_mm,
color = ~species,
text = ~paste("Body Mass:", body_mass_g),
type = 'scatter', mode = 'markers') %>%
add_markers() %>% highlight("plotly_selected")
# Map with leaflet
penguin_map <- leaflet(sd_penguins) %>% addTiles() %>%
addCircles(lng = ~longitude, lat = ~latitude,
weight = 1, radius = 40, popup = ~species)
# Combine both using crosstalk
bscols(widths = c(6, 6), penguin_plot, penguin_map)
crosstalk
BitSpatial
library(tidyverse);library(sf);library(bitSpatial);library(ggiraph)
start_gg <- bitSpatial::admi |>
ggplot(aes(geometry = geometry)) +
geom_sf(
data = bitSpatial::mega,
aes(fill = mega_nm), color = 'black', linewidth = 0.5
) +
geom_sf(fill = NA, color = 'black', linewidth = 0.1)
# 인터랙티브 지도
map_gg <- bitSpatial::admi |>
ggplot(aes(geometry = geometry)) +
geom_sf(
data = bitSpatial::mega,
aes(fill = mega_nm), color = 'black', linewidth = 0.5 ) +
geom_sf(fill = NA, color = 'black', linewidth = 0.1 ) +
geom_sf_interactive(fill = NA,
aes(data_id = cty_cd, tooltip = glue::glue('{mega_nm}<br>{cty_nm}')),
linewidth = 0.1
) +
theme_void() +
theme(legend.position = 'none') +
scale_fill_manual(
values = c("#A0CBE8FF", "#F28E2BFF", "#FFBE7DFF", "#59A14FFF", "#8CD17DFF", "#B6992DFF", "#F1CE63FF", "#499894FF", "#86BCB6FF", "#E15759FF", "#FF9D9AFF", "#79706EFF", "#BAB0ACFF", "#D37295FF", "#B07AA1FF", "#D4A6C8FF", "#9D7660FF") )
girafe(ggobj = map_gg,
options = list(opts_hover(
css = girafe_css(css = '', area = 'stroke: black; fill: black;'))))
BitSpatial
library(tidyverse)
dat <- tribble( ~neo_bank, ~market_cap_bn, ~logo_url,
"Nubank", 45, "https://upload.wikimedia.org/wikipedia/commons/f/f7/Nubank_logo_2021.svg",
"Revolut", 33, "https://upload.wikimedia.org/wikipedia/commons/d/d6/Revolut.svg",
"Chime", 25, "https://upload.wikimedia.org/wikipedia/commons/f/f6/Chime_company_logo.svg",
"WeBank", 21, "https://upload.wikimedia.org/wikipedia/en/e/eb/WeBank_Logo.svg",
"Kakaobank", 19, "https://upload.wikimedia.org/wikipedia/commons/4/48/KakaoBank_logo.svg",
"Robinhood", 10, "https://upload.wikimedia.org/wikipedia/commons/d/da/Robinhood_%28company%29_logo.svg",
"SoFi", 10, "https://upload.wikimedia.org/wikipedia/commons/1/16/SoFi_logo.svg",
"Tinkoff", 9, "https://upload.wikimedia.org/wikipedia/commons/1/19/Tinkoff_logo_2024.svg",
"N26", 9, "https://upload.wikimedia.org/wikipedia/commons/b/bf/N26_logo_2019.svg",
"K Bank", 7, "https://upload.wikimedia.org/wikipedia/commons/2/26/Kbank_logo.svg"
)
map(dat$logo_url, magick::image_read_svg) |>
walk2(
dat$neo_bank,
\(x, y) magick::image_write(x, path = paste0("img/logos/", y, ".png"))
)
dat |>
mutate(neo_bank = fct_reorder(neo_bank, market_cap_bn)) |>
ggplot(aes(x = market_cap_bn, y = neo_bank)) +
geom_col() +
scale_y_discrete(
labels = \(x) glue::glue("<img src='img/logos/{x}.png' height=20 />")
) +
theme_minimal(base_size = 12, base_family = 'Source Sans Pro') +
labs(
title = 'Top 10 neobanks worldwide by market capitalization 2022',
y = element_blank()) +
theme(axis.text.y = ggtext::element_markdown())
library(tidyverse); library(palmerpenguins); library(ggiraph); library(patchwork)
color_palette <- thematic::okabe_ito(3)
scatter_gg <- penguins |> drop_na() |>
ggplot(aes(x = flipper_length_mm, y = body_mass_g, color = species, data_id = species)) +
geom_point_interactive(size = 1) +
labs(title = "펭귄의 지느러미 길이와 몸무게",
x = "지느러미 길이 (mm)", y = "몸무게 (g)", color = "종") +
theme_minimal(base_size = 5) +
theme(legend.position = "top") +
scale_color_manual(values = color_palette) +
scale_y_continuous(labels = scales::comma)
boxplot_gg <- penguins |> drop_na() |>
ggplot(aes(x = species, y = body_mass_g, fill = species, data_id = species)) +
geom_boxplot_interactive() +
geom_jitter_interactive(aes(col = species)) +
labs(title = "펭귄 종별 몸무게",
x = "펭귄 종명", y = "몸무게 (g)", color = "종") +
theme_minimal(base_size = 12) +
theme(legend.position = "none") +
scale_fill_manual(values = color_palette) +
scale_color_manual(values = color_palette) +
scale_y_continuous(labels = scales::comma) +
coord_flip()
girafe(
ggobj = boxplot_gg + plot_spacer() + scatter_gg + plot_layout(widths = c(0.45, 0.1, 0.45)),
options = list(
opts_hover(css = ''), opts_hover_inv(css = "opacity:0.1;"),
opts_sizing(rescale = FALSE)
))
library(tidyverse)
library(rvest)
moon_html <- read_html("https://ko.wikipedia.org/wiki/문재인의_대통령_순방_목록")
moon_raw <- moon_html |>
html_elements("table") |>
html_table()
moon_h3 <- moon_html |>
html_elements("h3") |>
html_text()
names(moon_raw) <- moon_h3[1:6]
moon_tbl <- moon_h3 |>
enframe() |>
mutate(연도 = str_remove(value, "년\\[편집\\]")) |>
filter(str_detect(연도, "\\d{4}")) |>
mutate(data = moon_raw[c(1:5)]) |>
unnest(data) |>
select(연도, 나라, 장소, 일자, 세부내용=`세부 내용`) |>
separate(일자, into = c("출국", "도착"), sep = "~") |>
mutate(도착 = ifelse(is.na(도착), 출국, 도착)) |>
mutate(출국일 = str_glue("{연도}-{출국}"),
도착일 = str_glue("{연도}-{도착}")) |>
mutate(출국일 = parse_date(출국일, "%Y-%m월 %d일"),
도착일 = parse_date(도착일, "%Y-%m월 %d일")) |>
select(출국일, 도착일, 국가 = 나라, 장소) |>
mutate(기간 = (출국일 %--% 도착일) / ddays(1) +1)
moon_tbl |> slice(1:5) |> gt::gt()
출국일 | 도착일 | 국가 | 장소 | 기간 |
---|---|---|---|---|
2017-06-28 | 2017-07-03 | 미국 | 워싱턴 D.C. | 6 |
2017-07-05 | 2017-07-08 | 독일 | 베를린, 함부르크 | 4 |
2017-09-06 | 2017-09-07 | 러시아 | 블라디보스토크 | 2 |
2017-09-19 | 2017-09-21 | 미국 | 뉴욕 | 3 |
2017-11-08 | 2017-11-10 | 인도네시아 | 자카르타 | 3 |
library(countrycode)
library(gt)
library(gtExtras)
country_codes <- data.frame(
"국가" = c("노르웨이", "뉴질랜드", "덴마크", "독일", "라오스", "러시아", "말레이시아", "미국", "미얀마", "바티칸 시국", "베트남", "벨기에", "브루나이", "사우디아라비아", "스웨덴", "스페인", "싱가포르", "아랍에미리트", "아르헨티나", "영국", "오스트레일리아", "오스트리아", "우즈베키스탄", "이집트", "이탈리아", "인도", "인도네시아", "일본", "조선민주주의인민공화국", "중화인민공화국", "체코", "카자흐스탄", "캄보디아", "태국", "투르크메니스탄", "파푸아뉴기니", "프랑스", "핀란드", "필리핀", "헝가리"),
"영문_국가명" = c("Norway", "New Zealand", "Denmark", "Germany", "Laos", "Russia", "Malaysia", "United States", "Myanmar", "Vatican City", "Vietnam", "Belgium", "Brunei", "Saudi Arabia", "Sweden", "Spain", "Singapore", "United Arab Emirates", "Argentina", "United Kingdom", "Australia", "Austria", "Uzbekistan", "Egypt", "Italy", "India", "Indonesia", "Japan", "North Korea", "China", "Czech Republic", "Kazakhstan", "Cambodia", "Thailand", "Turkmenistan", "Papua New Guinea", "France", "Finland", "Philippines", "Hungary"),
"iso2c" = c("NO", "NZ", "DK", "DE", "LA", "RU", "MY", "US", "MM", "VA", "VN", "BE", "BN", "SA", "SE", "ES", "SG", "AE", "AR", "GB", "AU", "AT", "UZ", "EG", "IT", "IN", "ID", "JP", "KP", "CN", "CZ", "KZ", "KH", "TH", "TM", "PG", "FR", "FI", "PH", "HU")
)
moon_tbl |>
group_by(국가) |>
summarise(방문수 = n(),
연도 = str_c(str_glue("{lubridate::year(출국일)}"), collapse = ",")) |>
left_join(country_codes, by = "국가") |>
mutate(대륙 = countrycode(iso2c, "iso2c", "continent")) |>
arrange(desc(방문수)) |>
select(-영문_국가명) |>
select(iso2c, everything()) |>
filter(대륙 %in% c("Americas", "Oceania")) |>
gt(groupname_col = "대륙") |>
fmt_flag(columns = iso2c) |>
cols_label(
iso2c = ""
) |>
cols_align("center") |>
tab_header(
title = md("문재인 대통령 해외 순방"),
subtitle = md("2017년 5월 10일 ~ 2021년 5월 9일")
) |>
tab_footnote(
footnote = md("데이터 출처: [위키백과](https://ko.wikipedia.org/wiki/문재인의_대통령_순방_목록)")
) |>
gt_theme_538() |>
## 표 전체 합계 -------------------------------------
grand_summary_rows(
columns = 방문수,
fns = list(label = "", fn = "sum"),
fmt = ~ fmt_integer(.),
side = "bottom"
)
문재인 대통령 해외 순방 | ||||
---|---|---|---|---|
2017년 5월 10일 ~ 2021년 5월 9일 | ||||
국가 | 방문수 | 연도 | ||
Americas | ||||
미국 | 8 | 2017,2017,2018,2018,2019,2019,2021,2021 | ||
아르헨티나 | 1 | 2018 | ||
Oceania | ||||
뉴질랜드 | 1 | 2018 | ||
오스트레일리아 | 1 | 2021 | ||
파푸아뉴기니 | 1 | 2018 | ||
— | — | 12 | — | |
데이터 출처: 위키백과 |
library(viridis); library(sf); sf_use_s2(FALSE)
moon_tour <- moon_tbl |> group_by(국가) |>
summarise(방문수 = n(),
연도 = str_c(str_glue("{lubridate::year(출국일)}"), collapse = ",")) |>
left_join(country_codes, by = "국가")
world_map <- rnaturalearth::ne_countries(scale = 50, returnclass = "sf")
country_centroid <- inner_join(st_centroid(world_map), moon_tour,
by = c("iso_a2" = "iso2c"))
country_centroid <- bind_cols(country_centroid,
st_coordinates(country_centroid) |>
as_tibble() |> set_names(c("lon", "lat")))
moon_map <- left_join(world_map, moon_tour, by = c("iso_a2" = "iso2c"))
moon_map |>
filter(continent != "Antarctica") |>
mutate(방문수 = factor(방문수, levels = c(1, 2, 8),
labels = c("1회", "2회", "8회"))) |>
ggplot() +
geom_sf(aes(fill = 방문수)) +
scale_fill_manual(values = c("gray50", "skyblue", "blue"),
na.value = "transparent",
breaks = c("1회", "2회", "8회", NA),
labels = c("1회", "2회", "8회", "방문 없음")) +
labs(title = "문재인 대통령 해외순방") +
theme_void(base_family = "NanumGothic") +
theme(legend.position = "bottom") +
ggrepel::geom_text_repel(
data = country_centroid |> select(국가, lon, lat) |> st_drop_geometry(),
aes(label = 국가, x = lon, y = lat), size = 3, segment.size = 0.2)
offset <- 180 - 150
polygon <- st_polygon(x = list(rbind(
c(-0.0001 - offset, 90), c(0 - offset, 90),
c(0 - offset, -90), c(-0.0001 - offset, -90),
c(-0.0001 - offset, 90) ))) %>%
st_sfc() %>% st_set_crs(4326)
world_pacific <- world_map |>
st_difference(polygon) |> st_transform(crs = "+proj=eqc +x_0=0 +y_0=0 +lat_0=0 +lon_0=150")
moon_pacific_map <- left_join(world_pacific, moon_tour, by = c("iso_a2" = "iso2c"))
pacific_centroid <- inner_join(st_centroid(moon_pacific_map), moon_tour, by = c("iso_a2" = "iso2c"))
pacific_country_centroid <- bind_cols(pacific_centroid,
st_coordinates(pacific_centroid) |> as_tibble() |> set_names(c("lon", "lat")))
moon_pacific_map |>
mutate(방문수 = factor(방문수, levels = c(1, 2, 8), labels = c("1회", "2회", "8회"))) |>
ggplot() +
geom_sf(aes(fill = 방문수)) +
scale_fill_manual(values = c("gray50", "skyblue", "blue"),
na.value = "transparent",
breaks = c("1회", "2회", "8회", NA),
labels = c("1회", "2회", "8회", "방문 없음")) +
theme_void(base_family = "NanumGothic") +
theme(legend.position = "bottom") +
ggrepel::geom_text_repel(
data = pacific_country_centroid |> select(국가.x, lon, lat) |> st_drop_geometry(),
aes(label = 국가.x, x = lon, y = lat), size = 3, segment.size = 0.2)
Open AI Code Interpreter → Advanced Data Analysis → 챗GPT4
Tools -> Global Options -> Copilot -> Enable Github Copilot
ggplot
데이터 시각화library(bitSpatial)
bitSpatial::admi |>
select(base_ym, mega_nm, cty_nm, admi_nm, geometry)
#> Simple feature collection with 3528 features and 4 fields
#> Geometry type: GEOMETRY
#> Dimension: XY
#> Bounding box: xmin: 124.6117 ymin: 33.11238 xmax: 130.9401 ymax: 38.61369
#> Geodetic CRS: WGS 84
#> # A tibble: 3,528 × 5
#> base_ym mega_nm cty_nm admi_nm geometry
#> <chr> <chr> <chr> <chr> <POLYGON [°]>
#> 1 202306 서울특별시 종로구 사직동 ((126.9771 37.5758, 126.9744 37.57607, 1…
#> 2 202306 서울특별시 종로구 삼청동 ((126.9736 37.59327, 126.9756 37.58968, …
#> 3 202306 서울특별시 종로구 부암동 ((126.9772 37.59865, 126.9711 37.60078, …
#> 4 202306 서울특별시 종로구 평창동 ((126.98 37.62828, 126.975 37.62903, 126…
#> 5 202306 서울특별시 용산구 한남동 ((127.0057 37.5477, 127.0043 37.5502, 12…
#> 6 202306 서울특별시 성동구 왕십리2동 ((127.0336 37.56263, 127.0268 37.56496, …
#> 7 202306 서울특별시 성동구 마장동 ((127.0381 37.57227, 127.0322 37.57009, …
#> 8 202306 서울특별시 중구 중림동 ((126.9709 37.55937, 126.9695 37.56198, …
#> 9 202306 서울특별시 성동구 옥수동 ((127.0154 37.54825, 127.0131 37.55005, …
#> 10 202306 서울특별시 중구 장충동 ((127.0096 37.56303, 127.0012 37.56286, …
#> # ℹ 3,518 more rows
#| label: shinylive-esquisse
#| viewerWidth: 600
#| viewerHeight: 500
#| standalone: true
library(esquisse)
library(shiny)
library(ggplot2)
library(showtext)
showtext_auto()
ui <- fluidPage(
titlePanel("Graph with esquisse"),
sidebarLayout(
sidebarPanel(
fileInput("file", "Choose CSV File",
accept = c("text/csv", "text/comma-separated-values,text/plain", ".csv")),
radioButtons(
inputId = "data",
label = "Select data to use:",
choices = c("mpg", "diamonds", "economics", "Uploaded Data" = "uploaded_data")
)
),
mainPanel(
tabsetPanel(
tabPanel(
title = "esquisse",
esquisse_ui(
id = "esquisse",
header = FALSE # dont display gadget title
)
),
tabPanel(
title = "output",
tags$b("Code:"),
verbatimTextOutput("code"),
tags$b("Filters:"),
verbatimTextOutput("filters"),
tags$b("Data:"),
verbatimTextOutput("data")
)
)
)
)
)
server <- function(input, output, session) {
data_r <- reactiveValues(data = iris, name = "iris")
observeEvent(input$file, {
req(input$file)
data_r$data <- read.csv(input$file$datapath)
data_r$name <- "uploaded_data"
})
observe({
if (input$data != "uploaded_data") {
data_r$data <- get(input$data)
data_r$name <- input$data
}
})
results <- esquisse_server(
id = "esquisse",
data_rv = data_r
)
output$code <- renderPrint({
results$code_plot
})
output$filters <- renderPrint({
results$code_filters
})
output$data <- renderPrint({
str(results$data)
})
}
shinyApp(ui, server)