1. Giới thiệu

Phần này sẽ chỉ cho ta cách sử dụng trực quan hóa và chuyển đổi để khám phá dữ liệu của ta một cách có hệ thống, một nhiệm vụ mà các nhà thống kê gọi là phân tích dữ liệu khám phá, hay viết tắt là EDA. EDA là một chu kỳ lặp đi lặp lại. Công việc :

EDA là một phần quan trọng của bất kỳ phân tích dữ liệu nào, ngay cả khi các câu hỏi được giao cho ta trên đĩa, bởi vì luôn cần điều tra chất lượng dữ liệu của mình. Làm sạch dữ liệu chỉ là một ứng dụng của EDA: ta đặt câu hỏi về việc liệu dữ liệu của ta có đáp ứng mong đợi của ta hay không. Để làm sạch dữ liệu, ta sẽ cần triển khai tất cả các công cụ của EDA: trực quan hóa, chuyển đổi và lập mô hình. Trong phần này ,chúng ta sẽ kết hợp những gì đã học về dplyr và ggplot2 để đặt câu hỏi một cách tương tác, trả lời chúng bằng dữ liệu và sau đó đặt câu hỏi mới.

2. Câu hỏi

Mục tiêu trong EDA là phát triển sự hiểu biết về dữ liệu của mình.

EDA về cơ bản là một quá trình sáng tạo. Và giống như hầu hết các quy trình sáng tạo, chìa khóa để đặt câu hỏi chất lượng là tạo ra một lượng lớn câu hỏi.

Không có quy tắc nào về những câu hỏi, ta nên hỏi để hướng dẫn nghiên cứu của mình. Tuy nhiên, hai loại câu hỏi sẽ luôn hữu ích để khám phá dữ liệu. Ta có thể nói một cách lỏng lẻo những câu hỏi này như:

Phần còn lại của chương này sẽ xem xét hai câu hỏi này.

3. Biến thể

Sự thay đổi là xu hướng của các giá trị của một biến thay đổi từ phép đo này sang phép đo khác. Ta có thể thấy sự khác biệt dễ dàng trong cuộc sống thực; nếu ta đo bất kỳ biến liên tục nào hai lần, ta sẽ nhận được hai kết quả khác nhau. Mỗi biến có mô hình biến thể riêng, có thể tiết lộ thông tin thú vị. Cách tốt nhất để hiểu mẫu đó là trực quan hóa sự phân bố các giá trị của biến.

3.1 Trực quan hóa các bản phân phối

Ta hình dung phân phối của một biến sẽ phụ thuộc vào việc biến đó là phân loại hay liên tục. Một biến là phân loại nếu nó chỉ có thể nhận một trong một tập hợp nhỏ các giá trị. Trong R, các biến phân loại thường được lưu dưới dạng các thừa số hoặc vectơ ký tự. Để kiểm tra phân phối của một biến phân loại, hãy sử dụng biểu đồ thanh:

ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut))

Chiều cao của các thanh hiển thị số lượng quan sát xảy ra với mỗi giá trị x. Ta có thể tính toán các giá trị này theo cách thủ công với dply::count() :

diamonds %>%
  count(cut)

Một biến là liên tục nếu nó có thể nhận bất kỳ tập hợp vô hạn các giá trị được sắp xếp. Số và ngày giờ là hai ví dụ về biến liên tục. Để kiểm tra phân phối của một biến liên tục, hãy sử dụng biểu đồ:

ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = carat), binwidth = .5)

ta có thể tính toán điều này bằng tay bằng cách kết hợp dply::count() và \(ggplot2::cut\_width()\)

diamonds %>%
  count(cut_width(carat, .5))

Biểu đồ chia trục x thành các ngăn cách đều nhau và sau đó sử dụng chiều cao của thanh để hiển thị số lượng quan sát rơi vào từng ngăn. Trong biểu đồ trên, thanh cao nhất cho thấy gần 30.000 quan sát có giá trị varat nằm trong khoảng từ 0,25 đến 0,75, là cạnh trái và phải của thanh.

Ta có thể đặt độ rộng của các khoảng trong biểu đồ tần suất với đối số băng thông , được đo bằng đơn vị của biến x . Ta phải luôn khám phá nhiều loại băng thông khi làm việc với biểu đồ, vì các băng thông khác nhau có thể hiển thị các mẫu khác nhau. Ví dụ: đây là biểu đồ bên trên trông như thế nào khi chúng ta chỉ phóng to những viên kim cương có kích thước nhỏ hơn ba carat và chọn một băng thông nhỏ.

diamonds %>%
  filter(carat < 3) %>%
  ggplot(mapping = aes(x = carat)) +
  geom_histogram(binwidth = .1)

Nếu ta muốn phủ nhiều biểu đồ trong cùng một biểu đồ, tôi khuyên ta nên sử dụng \(geom\_freqpoly()\) thay vì \(geom\_histogram()\) . \(geom\_freqpoly()\) thực hiện phép tính tương tự như \(geom\_histogram()\) , nhưng thay vì hiển thị số đếm bằng các thanh, thay vào đó hãy sử dụng các dòng. Việc hiểu các đường chồng chéo dễ dàng hơn nhiều so với các thanh.

diamonds %>%
  filter(carat < 3) %>%
  ggplot(mapping = aes(x = carat, color = cut)) +
  geom_freqpoly()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Có một vài thách thức với loại biểu đồ này, mà chúng ta sẽ quay lại để hình dung một biến phân loại và biến liên tục . Bây giờ chúng ta có thể hình dung sự thay đổi, làm thế nào để chúng ta đặt câu hỏi hay và khám phá thêm? Chìa khóa để đặt những câu hỏi tiếp theo hiệu quả là dựa trên sự tò mò của ta (Ta muốn tìm hiểu thêm về điều gì?) cũng như sự hoài nghi của ta (Làm sao điều này có thể gây hiểu lầm được?).

3.2 Các giá trị điển hình

Trong cả biểu đồ thanh và biểu đồ, các thanh cao hiển thị các giá trị phổ biến của một biến và các thanh ngắn hơn hiển thị các giá trị ít phổ biến hơn. Những nơi không có thanh hiển thị các giá trị không được nhìn thấy trong dữ liệu của mình. Để biến thông tin này thành những câu hỏi hữu ích, hãy tìm bất cứ điều gì bất ngờ:

  • Những giá trị nào là phổ biến nhất? Tại sao?

  • Những giá trị nào là hiếm? Tại sao? Điều đó có phù hợp với mong đợi của mình không?

  • Ta có thể thấy bất kỳ mô hình bất thường? Điều gì có thể giải thích chúng?

Ví dụ, biểu đồ bên dưới gợi ý một số câu hỏi thú vị:

  • Tại sao lại có nhiều viên kim cương nguyên carat và một phần nhỏ carat thông thường?

  • Tại sao có nhiều viên kim cương ở bên phải của mỗi đỉnh hơn một chút ở bên trái của mỗi đỉnh?

  • Tại sao không có viên kim cương nào lớn hơn 3 carat?

    smaller <- diamonds
    ggplot(data = smaller, mapping = aes(x = carat)) +
      geom_histogram(binwidth = .01)

Các cụm giá trị tương tự gợi ý rằng các nhóm con tồn tại trong dữ liệu của ta. Để hiểu các nhóm nhỏ, hãy hỏi:

  • Làm thế nào là các quan sát trong mỗi cụm tương tự nhau?

  • Các quan sát trong các cụm riêng biệt khác nhau như thế nào?

  • Làm thế nào ta có thể giải thích hoặc mô tả các cụm?

  • Tại sao sự xuất hiện của các cụm có thể gây hiểu nhầm?

Biểu đồ dưới đây cho thấy độ dài (tính bằng phút) của 272 lần phun trào của mạch nước phun Old Faithful ở Công viên Quốc gia Yellowstone. Các vụ phun trào dường như được nhóm thành hai nhóm: những vụ phun trào kéo dài khoảng 2 phút (ngắn) và những vụ phun trào dài hơn khoảng 4-5 phút, nhưng ít ở giữa.

    ggplot(data = faithful, mapping = aes(x = eruptions)) +
      geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

3.3 Các giá trị linh động

Ngoại lệ là những quan sát không bình thường; điểm dữ liệu dường như không phù hợp với mẫu. Đôi khi ngoại lệ là lỗi nhập dữ liệu; những lần khác, ngoại lệ gợi ý khoa học mới quan trọng. Khi ta có nhiều dữ liệu, các giá trị ngoại lệ đôi khi rất khó nhìn thấy trong biểu đồ. Ví dụ: lấy phân phối của biến y từ bộ dữ liệu kim cương. Bằng chứng duy nhất về các giá trị ngoại lai là các giới hạn rộng bất thường trên trục x.

ggplot(data = diamonds, mapping = aes(x = y)) +
  geom_histogram(binwidth = .5)

Có rất nhiều quan sát trong các thùng thông thường mà các thùng hiếm quá ngắn đến mức ta không thể nhìn thấy chúng. Để dễ dàng nhìn thấy các giá trị bất thường, chúng ta cần phóng to các giá trị nhỏ của trục y bằng \(coord\_cartesian()\) :

ggplot(data = diamonds, mapping = aes(x = y)) +
  geom_histogram() +
  coord_cartesian(ylim = c(0, 50))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Điều này cho phép ta thấy rằng có ba giá trị bất thường: 0, ~30 và ~60. Ta sẽ gỡ nó ra bằng dplyr:

unusual <- diamonds %>%
  filter(y < 3 | y > 20) %>%
  select(price, x, y, z) %>%
  arrange(y)

unusual

Biến y đo một trong ba kích thước của những viên kim cương này, tính bằng mm. Ta biết rằng kim cương không thể có chiều rộng bằng 0 mm, vì vậy các giá trị này phải không chính xác. Ta cũng có thể nghi ngờ rằng các phép đo 32mm và 59mm là không hợp lý: những viên kim cương đó dài hơn một inch, nhưng không có giá hàng trăm nghìn đô la!

Ta nên lặp lại phân tích của mình có và không có ngoại lệ. Nếu chúng có ảnh hưởng tối thiểu đến kết quả và ta không thể hiểu tại sao chúng lại ở đó, ta nên thay thế chúng bằng các giá trị còn thiếu và tiếp tục. Tuy nhiên, nếu chúng có ảnh hưởng đáng kể đến kết quả của mình, ta không nên bỏ chúng mà không có lý do chính đáng. Ta sẽ cần tìm ra nguyên nhân gây ra chúng (ví dụ: lỗi nhập dữ liệu) và tiết lộ rằng ta đã xóa chúng trong bài viết của mình.

##3.4 Bài tập

  1. Khám phá phân phối của từng biến x , yz trong kim cương . Ta học gì? Hãy nghĩ về một viên kim cương và cách ta có thể quyết định kích thước nào là chiều dài, chiều rộng và chiều sâu.
# Tạo dữ liệu mẫu
set.seed(123)
filtered <- data.frame(x = rnorm(1000), y = rnorm(1000), z = rnorm(1000))

# Chọn các quan sát thỏa mãn điều kiện x > -1 & y < 1 & z > -0.5
filtered <- filter(filtered, x > -1 & y < 1 & z > -0.5)
# Định nghĩa đối tượng filtered và các biến x, y, z
summary(select(filtered, x, y, z))
##        x                 y                 z          
##  Min.   :-0.9968   Min.   :-2.6953   Min.   :-0.4937  
##  1st Qu.:-0.2785   1st Qu.:-0.8213   1st Qu.:-0.1113  
##  Median : 0.1653   Median :-0.1631   Median : 0.3359  
##  Mean   : 0.2795   Mean   :-0.2632   Mean   : 0.4424  
##  3rd Qu.: 0.7409   3rd Qu.: 0.4260   3rd Qu.: 0.8374  
##  Max.   : 3.2410   Max.   : 0.9935   Max.   : 2.7352
# Vẽ biểu đồ histogram cho biến "x"
filtered %>%
  filter(x < 10) %>%
  ggplot(mapping = aes(x = x)) +
  geom_histogram(binwidth = .01) +
  scale_x_continuous(breaks = 1:10)

# Vẽ biểu đồ histogram cho biến "y"
filtered %>%
  filter(y < 10) %>%
  ggplot(mapping = aes(x = y)) +
  geom_histogram(binwidth = .01) +
  scale_x_continuous(breaks = 1:10)

# Vẽ biểu đồ histogram cho biến "z"
filtered %>%
  filter(z < 10) %>%
  ggplot(mapping = aes(x = z)) +
  geom_histogram(binwidth = .01) +
  scale_x_continuous(breaks = 1:10)

  1. Khám phá sự phân phối của giá cả . Ta có phát hiện ra điều gì bất thường hoặc đáng ngạc nhiên không? (Gợi ý: Hãy suy nghĩ cẩn thận về băng thông và đảm bảo rằng ta đã thử nhiều loại giá trị.)
summary(select(diamonds, price))
##      price      
##  Min.   :  326  
##  1st Qu.:  950  
##  Median : 2401  
##  Mean   : 3933  
##  3rd Qu.: 5324  
##  Max.   :18823
sd(diamonds$price)
## [1] 3989.44

Có một lượng lớn sự khác biệt giữa giá của những viên kim cương “rẻ nhất” và đắt nhất. Trên thực tế, viên kim cương đắt nhất lớn hơn 3 độ lệch chuẩn so với phần tư thứ 3.

ggplot(data = diamonds, mapping = aes(x = price)) +
  geom_histogram(binwidth = 1000)

ggplot(data = diamonds, mapping = aes(x = price)) +
  geom_histogram(binwidth = 100)

Phân phối của giá tương tự như phân phối của x, y và z. Dữ liệu bị lệch phải với hầu hết các viên kim cương có giá dưới 5.000 đô la và cụm kim cương lớn nhất có giá dưới 2.500 đô la. Bắt đầu từ ~2.500 đô la, độ dốc của mức giảm trở nên rất dễ đoán. Về cơ bản, khi giá tăng khoảng 1.000 đô la, số lượng kim cương sẽ giảm theo một lượng tương ứng.

Biểu đồ với băng thông hẹp hơn thể hiện một số điểm đặc biệt. Dường như có sự đảo ngược xu hướng đối với những viên kim cương có giá lên tới 5.000 đô la và một số ít kim cương kỳ lạ có giá ~1.250 đô la.

  1. Có bao nhiêu viên kim cương là 0,99 carat? 1 carat bằng bao nhiêu? Ta nghĩ đâu là nguyên nhân của sự khác biệt?
nrow(filter(diamonds, carat == .99))
## [1] 23
nrow(filter(diamonds, carat == 1))
## [1] 1558

Tôi không chắc nguyên nhân là gì? Có lẽ phù phiếm? Mọi người muốn ít nhất một cara cho viên kim cương đáng mơ ước? Cũng có thể có một khoản phí bảo hiểm đối với những viên kim cương đầy đủ carat.

Sự phân bố của các carat cho thấy rằng các carat đạt đỉnh tại hoặc gần các khoảng 0,5.

ggplot(data = diamonds, mapping = aes(x = carat)) +
  geom_histogram(binwidth = .1)

diamonds %>%
  filter(carat >= .9 & carat <= 1.1) %>%
  count(carat) %>%
  print(n = Inf)
## # A tibble: 21 × 2
##    carat     n
##    <dbl> <int>
##  1  0.9   1485
##  2  0.91   570
##  3  0.92   226
##  4  0.93   142
##  5  0.94    59
##  6  0.95    65
##  7  0.96   103
##  8  0.97    59
##  9  0.98    31
## 10  0.99    23
## 11  1     1558
## 12  1.01  2242
## 13  1.02   883
## 14  1.03   523
## 15  1.04   475
## 16  1.05   361
## 17  1.06   373
## 18  1.07   342
## 19  1.08   246
## 20  1.09   287
## 21  1.1    278
  1. So sánh và đối chiếu \(coord\_cartesian()\) với xlim() hoặc ylim() khi phóng to biểu đồ. Điều gì xảy ra nếu ta không đặt băng thông nhị phân? Điều gì xảy ra nếu ta thử và phóng to để chỉ hiển thị nửa thanh?
ggplot(data = diamonds, mapping = aes(x = x)) +
  geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data = diamonds, mapping = aes(x = x)) +
  geom_histogram() +
  coord_cartesian(xlim = c(2, 10), ylim = c(0, 5000))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = x)) +
  xlim(3, 9) +
  ylim(0, 5000)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 50 rows containing non-finite values (`stat_bin()`).
## Warning: Removed 1 rows containing missing values (`geom_bar()`).

4. Giá trị thiếu / trống

Nếu ta gặp phải các giá trị bất thường trong tập dữ liệu của mình và chỉ muốn chuyển sang phần còn lại của phân tích, thì ta có hai tùy chọn.

  1. Bỏ toàn bộ hàng với các giá trị lạ:
diamonds2 <- diamonds %>%
  filter(between(y, 3, 20))

Tôi không khuyến nghị tùy chọn này vì chỉ vì một phép đo không hợp lệ, không có nghĩa là tất cả các phép đo đều như vậy. Ngoài ra, nếu ta có dữ liệu chất lượng thấp, theo thời gian ta đã áp dụng phương pháp này cho mọi biến, ta có thể thấy rằng mình không còn bất kỳ dữ liệu nào!

  1. Thay vào đó, tôi khuyên ta nên thay thế các giá trị bất thường bằng các giá trị bị thiếu. Cách dễ nhất để làm điều này là sử dụng mutate() để thay thế biến bằng một bản sao đã sửa đổi. Ta có thể sử dụng hàm ifelse() để thay thế các giá trị bất thường bằng NA :
diamonds2 <- diamonds %>%
  mutate(y = ifelse(y < 3 | y > 20, NA, y))

ifelse() có ba đối số. Kiểm tra đối số đầu tiên phải là một vectơ logic. Kết quả sẽ chứa giá trị của đối số thứ hai, , khi kiểm traTRUE và giá trị của đối số thứ ba, không khi nó là sai. Ngoài ifelse, hãy sử dụng \(dplyr::case\_when()\) . \(ase\_when\) đặc biệt hữu ích bên trong mutate khi ta muốn tạo một biến mới dựa trên sự kết hợp phức tạp của các biến hiện có.

Giống như R, ggplot2 tuân theo triết lý rằng các giá trị bị thiếu sẽ không bao giờ bị mất tích một cách âm thầm. Không rõ ta nên vẽ sơ đồ các giá trị bị thiếu ở đâu, vì vậy ggplot2 không đưa chúng vào sơ đồ, nhưng nó cảnh báo rằng chúng đã bị xóa:

ggplot(data = diamonds2, mapping = aes(x = x, y = y)) +
  geom_point()
## Warning: Removed 9 rows containing missing values (`geom_point()`).

Để chặn cảnh báo đó, hãy đặt na.rm = TRUE .

ggplot(data = diamonds2, mapping = aes(x = x, y = y)) +
  geom_point(na.rm = TRUE)

Những lần khác, ta muốn hiểu điều gì làm cho các quan sát có giá trị bị thiếu khác với các quan sát có giá trị được ghi lại. Ví dụ: trong \(nycflights13::flights\) , các giá trị bị thiếu trong biến \(dep\_time\) cho biết chuyến bay đã bị hủy. Vì vậy, ta có thể muốn so sánh thời gian khởi hành theo lịch trình cho thời gian bị hủy và không bị hủy. Ta có thể làm điều này bằng cách tạo một biến mới với is.na() .

library(nycflights13)
data("flights")
flights %>%
  mutate(
    cancelled = is.na(dep_time),
    sched_hour = sched_dep_time %/% 100,
    sched_min = sched_dep_time %% 100,
    sched_dep_time = sched_hour + sched_min / 60
    ) %>%
  ggplot(mapping = aes(x = sched_dep_time)) +
  geom_freqpoly(mapping = aes(color = cancelled, binwidth = 1/4))
## Warning in geom_freqpoly(mapping = aes(color = cancelled, binwidth = 1/4)):
## Ignoring unknown aesthetics: binwidth
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

4.1 Bài tập

  1. Điều gì xảy ra với các giá trị bị thiếu trong biểu đồ? Điều gì xảy ra với các giá trị bị thiếu trong biểu đồ thanh? Tại sao lại có một sự khác biệt?
diamonds %>%
  mutate(y = ifelse(y < 3 | y > 20, NA, y)) %>%
  ggplot(mapping = aes(x = y)) +
  geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 9 rows containing non-finite values (`stat_bin()`).

Khi tạo biểu đồ, biểu đồ sẽ loại bỏ các giá trị còn thiếu. Điều này có thể là do biểu đồ dựa trên dữ liệu (số) liên tục để biểu thị tần suất đếm theo thùng. Khi nó không biết giá trị, nó không biết bỏ nó vào thùng nào, vì vậy nó loại bỏ nó.

Ngoài ra,Trong biểu đồ thanh, đồ họa dựa trên dữ liệu phân loại và giả sử NA là một chuỗi ký tự biểu thị một danh mục khác.

diamonds %>%
  mutate(cut = ifelse(runif(n()) < .1, NA_character_, as.character(cut))) %>%
  ggplot(mapping = aes(x = cut)) +
  geom_bar()

  1. na.rm = TRUE làm gì trong mean()sum() .
diamonds %>%
  mutate(xyz = ifelse(z == 0, NA, z)) %>%
  select(x,y,z,xyz) %>%
  arrange(z) %>%
  summarise(
    total_xyz = sum(xyz, na.rm = TRUE),
    mean_xyz = mean(xyz, na.rm = TRUE)
  )

Đối với bài tập này, ta chỉ chuyển đổi tất cả các số “0” trong z thành “NA” và sau đó áp dụng tổng và nghĩa. Khi NA xuất hiện trong dữ liệu, các giá trị tổng và giá trị trung bình không chắc chắn về cách tính toán chúng để nó trả về giá trị NA. na.rm = TRUE loại bỏ các giá trị NA trước khi phép tính được thực hiện và sau đó trả về tổng và giá trị trung bình của cột mới.

5. Phép cộng biến

Nếu biến thể mô tả hành vi trong một biến, thì hiệp phương sai mô tả hành vi giữa các biến. Đồng biến là xu hướng cho các giá trị của hai hoặc nhiều biến thay đổi cùng nhau theo một cách có liên quan. Cách tốt nhất để phát hiện sự đồng biến là trực quan hóa mối quan hệ giữa hai hoặc nhiều biến. Cách ta làm điều đó một lần nữa sẽ phụ thuộc vào loại biến liên quan.

5.1 Biến phân loại và liên tục

Người ta thường muốn khám phá sự phân bố của một biến liên tục được chia nhỏ bởi một biến phân loại, như trong đa giác tần số trước đó. Giao diện mặc định của \(geom\_freqpoly()\) không hữu ích cho kiểu so sánh đó vì chiều cao được tính theo số đếm. Điều đó có nghĩa là nếu một trong các nhóm nhỏ hơn nhiều so với nhóm kia, thật khó để thấy sự khác biệt về hình dạng. Ví dụ: hãy khám phá xem giá của một viên kim cương thay đổi như thế nào theo chất lượng của nó:

ggplot(data = diamonds, mapping = aes(x = price)) +
  geom_freqpoly(mapping = aes(color = cut), binwidth = 500)

Thật khó để thấy sự khác biệt trong phân phối vì tổng số lượng khác nhau rất nhiều:

ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut))

Để so sánh dễ dàng hơn, chúng ta cần hoán đổi những gì được hiển thị trên trục y. Thay vì hiển thị số đếm, ta sẽ hiển thị mật độ , là số đếm được chuẩn hóa sao cho diện tích bên dưới mỗi đa giác tần số là một.

ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) +
  geom_freqpoly(mapping = aes(color = cut), binwidth = 500)
## Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(density)` instead.

Biểu đồ này rất khó để giải thích.

Một cách khác để hiển thị phân phối của một biến liên tục được chia nhỏ bởi một biến phân loại là boxplot. Boxplot là một loại tốc ký trực quan để phân phối các giá trị phổ biến trong giới thống kê . Mỗi boxplot bao gồm:

  • Một hộp trải dài từ phân vị thứ 25 của phân phối đến phân vị thứ 75, một khoảng cách được gọi là phạm vi liên vùng (IQR). Ở giữa hộp là một dòng hiển thị trung vị, tức là phân vị thứ 50, của phân phối. Ba đường này cho ta biết mức độ lan rộng của phân phối và liệu phân phối có đối xứng qua đường trung bình hay lệch về một bên hay không.

  • Các điểm trực quan hiển thị các quan sát giảm hơn 1,5 lần IQR từ một trong hai cạnh của hộp. Những điểm xa lạ này là bất thường vì vậy được vẽ riêng lẻ.

  • Một đường thẳng (hoặc râu ria) kéo dài từ mỗi đầu của hộp và đi đến điểm xa nhất trong phân phối.

Chúng ta hãy xem sự phân bổ giá bằng cách sử dụng \(geom\_boxplot()\) :

ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = cut, y = price))

Ta thấy ít thông tin hơn về phân phối, nhưng các ô vuông nhỏ gọn hơn nhiều để Ta có thể dễ dàng so sánh chúng hơn (và phù hợp hơn trên một ô). Nó ủng hộ phát hiện phản trực giác rằng kim cương chất lượng tốt hơn trung bình rẻ hơn! Trong các bài tập, ta sẽ được thử thách để tìm ra lý do tại sao.Giả định của tôi là những biểu đồ này chỉ cho ta biết điều gì đó về vết cắt của những viên kim cương chứ không nói gì về kích thước hoặc màu sắc của chúng. Có thể là có một phần lớn hơn đáng kể các viên kim cương lý tưởng tương đối nhỏ hơn so với các vết cắt khác.

cắt giảm là một yếu tố có trật tự: công bằng tệ hơn tốt, tệ hơn rất tốt, v.v. Nhiều biến phân loại không có thứ tự phức tạp như vậy, vì vậy ta có thể muốn sắp xếp lại thứ tự của chúng để hiển thị nhiều thông tin hơn. Một cách để làm điều đó là sử dụng chức năng sắp xếp lại () .

Ví dụ: lấy biến lớp trong tập dữ liệu mpg . Ta có thể muốn biết quãng đường đi trên đường cao tốc khác nhau như thế nào giữa các lớp:

ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
  geom_boxplot()

Để làm cho xu hướng dễ nhìn thấy hơn, chúng ta có thể sắp xếp lại lớp dựa trên giá trị trung bình của hwy :

ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))

Nếu có tên biến dài, \(geom\_boxplot()\) sẽ hoạt động tốt hơn nếu ta lật nó 90 độ. Ta có thể làm điều đó với \(coord\_flip()\) .

ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy)) +
  coord_flip()

5.1.1 Bài tập

  1. Sử dụng những gì tôi đã học để cải thiện khả năng hiển thị thời gian khởi hành của các chuyến bay bị hủy và không bị hủy.
library(nycflights13)
library(ggplot2)
flights <- flights %>%
  mutate(cancelled = ifelse(is.na(dep_time), 1, 0))
library(ggplot2)
flights %>%
  group_by(year, month, day) %>%
  summarise(prop_cancelled = mean(cancelled)) %>%
  ggplot() +
  geom_boxplot(mapping = aes(x = reorder(as.character(month), prop_cancelled, FUN = median), y = prop_cancelled))
## `summarise()` has grouped output by 'year', 'month'. You can override using the
## `.groups` argument.

flights %>%
  group_by(year, month) %>%
  summarise(
    prop_cancelled = mean(cancelled)) %>%
  arrange(prop_cancelled)
## `summarise()` has grouped output by 'year'. You can override using the
## `.groups` argument.
flights %>%
  mutate(
    sched_dep_hour = sched_dep_time %/% 100,
    sched_dep_min = sched_dep_time %% 100,
    sched_dep_time = sched_dep_hour + sched_dep_min / 60
  ) %>%
  ggplot() +
  geom_boxplot(mapping = aes(x = cancelled, y = sched_dep_time))
## Warning: Continuous x aesthetic
## ℹ did you forget `aes(group = ...)`?

flights %>%
  mutate(
    sched_dep_hour = sched_dep_time %/% 100,
    sched_dep_min = sched_dep_time %% 100,
    sched_dep_time = sched_dep_hour + sched_dep_min / 60
  ) %>%
  ggplot(mapping = aes(x = sched_dep_time, y = ..density..)) +
  geom_freqpoly(mapping = aes(color = cancelled))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: The following aesthetics were dropped during statistical transformation: colour
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?

Đa giác tần số và biểu đồ hộp về cơ bản hiển thị cùng một thứ, đó là phần lớn các chuyến bay bị hủy xảy ra vào cuối ngày, ~ 15 hoặc 3 giờ chiều. Tuy nhiên, xin lưu ý rằng chúng ta phải cung cấp đối số mật độ cho biểu đồ đa giác tần số để kiểm soát việc có nhiều chuyến bay không bị hủy hơn đáng kể so với các chuyến bay bị hủy.

  1. Biến số nào trong bộ dữ liệu kim cương là quan trọng nhất để dự đoán giá của một viên kim cương? Làm thế nào là biến đó tương quan với cắt? Tại sao sự kết hợp của hai mối quan hệ đó lại dẫn đến những viên kim cương chất lượng thấp hơn lại đắt hơn?
colnames(diamonds)
##  [1] "carat"   "cut"     "color"   "clarity" "depth"   "table"   "price"  
##  [8] "x"       "y"       "z"
summary(diamonds)
##      carat               cut        color        clarity          depth      
##  Min.   :0.2000   Fair     : 1610   D: 6775   SI1    :13065   Min.   :43.00  
##  1st Qu.:0.4000   Good     : 4906   E: 9797   VS2    :12258   1st Qu.:61.00  
##  Median :0.7000   Very Good:12082   F: 9542   SI2    : 9194   Median :61.80  
##  Mean   :0.7979   Premium  :13791   G:11292   VS1    : 8171   Mean   :61.75  
##  3rd Qu.:1.0400   Ideal    :21551   H: 8304   VVS2   : 5066   3rd Qu.:62.50  
##  Max.   :5.0100                     I: 5422   VVS1   : 3655   Max.   :79.00  
##                                     J: 2808   (Other): 2531                  
##      table           price             x                y         
##  Min.   :43.00   Min.   :  326   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.:56.00   1st Qu.:  950   1st Qu.: 4.710   1st Qu.: 4.720  
##  Median :57.00   Median : 2401   Median : 5.700   Median : 5.710  
##  Mean   :57.46   Mean   : 3933   Mean   : 5.731   Mean   : 5.735  
##  3rd Qu.:59.00   3rd Qu.: 5324   3rd Qu.: 6.540   3rd Qu.: 6.540  
##  Max.   :95.00   Max.   :18823   Max.   :10.740   Max.   :58.900  
##                                                                   
##        z         
##  Min.   : 0.000  
##  1st Qu.: 2.910  
##  Median : 3.530  
##  Mean   : 3.539  
##  3rd Qu.: 4.040  
##  Max.   :31.800  
## 
ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = carat, fill = cut), binwidth = .1) +
  coord_cartesian(xlim = c(0, 3))

ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut))

ggplot(data = diamonds, mapping = aes(x = color, y = price)) +
  geom_boxplot()

ggplot(data = diamonds, mapping = aes(x = clarity, y = price)) +
  geom_boxplot()

ggplot(data = diamonds, mapping = aes(x = x, y = price)) +
  geom_point() +
  xlim(c(3, 10))
## Warning: Removed 13 rows containing missing values (`geom_point()`).

ggplot(data = diamonds, mapping = aes(x = y, y = price)) +
  geom_point() +
  xlim(c(1, 12))
## Warning: Removed 9 rows containing missing values (`geom_point()`).

ggplot(data = diamonds, mapping = aes(x = z, y = price)) +
  geom_point() +
  xlim(c(1, 7.5))
## Warning: Removed 22 rows containing missing values (`geom_point()`).

Giả thuyết trước đây của ta là những viên kim cương lý tưởng được trình bày dưới mức trong dữ liệu thực sự không chính xác. Trong thực tế thì ngược lại. Những viên kim cương lý tưởng là phần dữ liệu phổ biến nhất và những viên kim cương công bằng thực sự là phần nhỏ nhất.

ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_point() +
  geom_smooth()
## `geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'

Biểu đồ phân tán của giá so với carat, x, y và z đều rất giống nhau khiến tôi tin rằng x, y và z có thể là yếu tố quyết định của carat? Trong trường hợp này, ta cho rằng carat sẽ tương quan với các biến này.

ggplot(data = diamonds, mapping = aes(x = x, y = carat)) +
  geom_point() +
  geom_smooth(se = FALSE) +
  xlim(c(3, 10))
## `geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'
## Warning: Removed 13 rows containing non-finite values (`stat_smooth()`).
## Warning: Removed 13 rows containing missing values (`geom_point()`).

ggplot(data = diamonds, mapping = aes(x = y, y = carat)) +
  geom_point() +
  geom_smooth(se = FALSE) +
  xlim(c(1, 15))
## `geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'
## Warning: Removed 9 rows containing non-finite values (`stat_smooth()`).
## Warning: Removed 9 rows containing missing values (`geom_point()`).

ggplot(data = diamonds, mapping = aes(x = z, y = carat)) +
  geom_point() +
  geom_smooth(se = FALSE) +
  xlim(c(1, 10))
## `geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'
## Warning: Removed 21 rows containing non-finite values (`stat_smooth()`).
## Warning: Removed 21 rows containing missing values (`geom_point()`).

Trên thực tế, carat và x, y và z đều có mối tương quan cao (đặc biệt là sau khi lọc ra các ngoại lệ có khả năng được xác định trước đó).

ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = cut, y = carat))

Boxplot ở trên cho thấy rằng các vết cắt lý tưởng có giá trị trung bình thấp nhất của carat. Điều này có nghĩa là mặc dù những viên kim cương lý tưởng, tất cả đều như nhau, đều đắt hơn, nhưng nhìn chung chúng cũng nhỏ hơn nhiều, trong khi những viên kim cương đẹp về trung bình là những viên kim cương “lớn nhất” trong bộ dữ liệu của ta.

  1. Cài đặt gói gstance và tạo một boxplot ngang. Điều này so sánh với việc sử dụng \(coord\_flip()\) như thế nào ?
ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = cut, y = carat)) +
  coord_flip()

library(ggplot2)
ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = carat, y = cut))

Về cơ bản, chúng làm điều tương tự, nhưng gói gstance giả định một biểu đồ nằm ngang. GGplots có thể làm điều tương tự mà ta chỉ cần chỉ định các trục hoặc sử dụng hướng để chỉ định.

  1. Một vấn đề với các boxplots là chúng được phát triển trong thời đại của các bộ dữ liệu nhỏ hơn nhiều và có xu hướng hiển thị một số lượng lớn các “giá trị bên ngoài”. Một cách tiếp cận để khắc phục vấn đề này là biểu đồ giá trị chữ cái. Cài đặt gói lvplot và thử sử dụng \(geom\_lv()\) để hiển thị phân phối giá so với cắt giảm. Mình học gì? Làm thế nào để ta giải thích các lô?
library(lvplot)
library(ggplot2)
ggplot(data = diamonds) +
  geom_lv(mapping = aes(x = cut, y = price))

Những gì chúng ta thấy từ biểu đồ giá trị chữ cái là trong khi nó hoạt động giống như biểu đồ hộp, nó sử dụng nhiều phần tư hơn và do đó đưa ra biểu diễn chính xác hơn về các phần tử của tập dữ liệu. Điều này đặc biệt hữu ích đối với các tập dữ liệu lớn hơn, theo lý thuyết, sẽ có nhiều ngoại lệ (về mặt tuyệt đối) hơn so với các tập dữ liệu nhỏ hơn.

  1. So sánh và đối chiếu \(geom\_violin()\) với \(geom\_histogram()\) có khía cạnh hoặc \(geom\_freqpoly()\) có màu . Những ưu và nhược điểm của từng phương pháp là gì?
ggplot(data = diamonds) +
  geom_violin(mapping = aes(x = price, y = cut))

ggplot(data = diamonds) +
  geom_freqpoly(mapping = aes(x = price, y = ..density.., color = cut))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = price)) +
  facet_wrap(~cut, nrow = 5, ncol = 1)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Biểu đồ khía cạnh là cách tốt nhất để so sánh số lượng thô của dữ liệu. Bằng cách này, ta muốn nói rằng rất dễ dàng để thấy rằng những viên kim cương “lý tưởng” là phổ biến nhất trong bộ dữ liệu. Đa giác tần số hơi khó diễn giải, đặc biệt là giữa các danh mục tương tự nhau; trong ví dụ này là kim cương cao cấp và rất tốt. Tuy nhiên, nó rất hữu ích trong việc xác định vết cắt nào có mật độ cao nhất ở một mức giá nhất định và theo dõi sự thay đổi giá. Ví dụ, rất dễ dàng để thấy tất cả giá hội tụ như thế nào khi số lượng kim cương giảm. Và biểu đồ vĩ cầm là lý tưởng để xem nơi có những điểm bất thường trong dữ liệu. Ví dụ, nơi có “phình” trong violin.

  1. Nếu ta có một tập dữ liệu nhỏ, đôi khi sẽ hữu ích khi sử dụng \(geom\_jitter()\) để xem mối quan hệ giữa biến liên tục và biến phân loại. Gói ggbeeswarm cung cấp một số phương thức tương tự như \(geom\_jitter()\) . Liệt kê chúng và mô tả ngắn gọn những gì mỗi người làm.

Hai \(geom\_beeswarm\)\(geom\_quasirandom\) đầu tiên tạo ra các ô là sự kết hợp giữa điểm và vĩ cầm. Về mặt chức năng, các vĩ cầm được tạo ra bằng cách sử dụng các điểm và sự khác biệt giữa hai phương pháp là chọn ngẫu nhiên các điểm trong hoặc giữa các danh mục. Trong mỗi geom, ta có thể chỉ định các phương pháp ngẫu nhiên hóa.

\(geom\_beeswarm\) : hoạt động tương tự như \(geom\_jitter\)

\(geom\_quasirandom\) : hoạt động tương tự như \(geom\_jitter\), nhưng nó ngẫu nhiên hóa các điểm trong danh mục để giảm hiện tượng ghi đè.

\(geom\_beeswarm\) : biểu đồ kiểu điểm violon để hiển thị các điểm chồng chéo. x phải rời rạc

\(geom\_quasirandom\)* : biểu đồ kiểu điểm violon để hiển thị các điểm chồng chéo. x phải rời rạc

5.2 Biến phân loại

Để trực quan hóa hiệp phương sai giữa các biến phân loại, ta sẽ cần đếm số lượng quan sát cho mỗi kết hợp. Một cách để làm điều đó là dựa vào \(geom\_count()\) tích hợp sẵn :

ggplot(data = diamonds) +
  geom_count(mapping = aes(x = cut, y = color))

Kích thước của mỗi vòng tròn trong biểu đồ hiển thị số lượng quan sát xảy ra ở mỗi tổ hợp giá trị. Sự hiệp phương sai sẽ xuất hiện dưới dạng mối tương quan chặt chẽ giữa các giá trị x cụ thể và các giá trị y cụ thể.

Một cách tiếp cận khác là tính toán số lượng với dplyr:

library(dplyr)
diamonds %>%
  group_by(cut, color) %>%
  summarise(
    cut_by_color = n()
  )
## `summarise()` has grouped output by 'cut'. You can override using the `.groups`
## argument.

HOẶC đếm

sau đó trực quan hóa với \(geom\_tile()\) và tính thẩm mỹ lấp đầy:

diamonds %>%
  count(color, cut) %>%
  ggplot() +
  geom_tile(mapping = aes(x = color, y = cut, fill = n))

5.2.1 Bài tập

  1. Làm cách nào ta có thể thay đổi tỷ lệ bộ dữ liệu đếm ở trên để hiển thị rõ ràng hơn sự phân bố của vết cắt trong màu hoặc màu trong vết cắt?
diamonds %>%
  group_by(color) %>%
  count(color, cut) %>%
  mutate(
    prop = n/sum(n)
    ) %>%
  ggplot(mapping = aes(x = color, y = cut)) +
  geom_tile(aes(fill = prop))

diamonds %>%
  group_by(cut) %>%
  count(cut, color) %>%
  mutate(
    prop = n/sum(n)
  ) %>%
  ggplot(mapping = aes(x = color, y = cut)) +
  geom_tile(aes(fill = prop))

  1. Sử dụng \(geom\_tile()\) cùng với dplyr để khám phá xem độ trễ trung bình của chuyến bay thay đổi như thế nào theo điểm đến và tháng trong năm. Điều gì làm cho biểu đồ khó đọc? Làm thế nào ta có thể cải thiện nó?
library(nycflights13)
flights %>%
  filter(!is.na(arr_delay), arr_delay > 0) %>%
  group_by(dest, month) %>%
  mutate(avg_delay = mean(arr_delay)) %>%
  ggplot(mapping = aes(x = factor(month), y = reorder(dest, distance))) +
  geom_tile(mapping = aes(fill = avg_delay)) +
  labs(x = "Month", y = "Destination", fill = "Average Delay")

Một vài điều làm cho biểu đồ khó đọc.

  1. Có quá nhiều điểm đến để tất cả các trường có thể nằm vừa vặn trên một trong hai trục.
  2. Biến tháng được chứa trong dữ liệu dưới dạng số nguyên khi nó thực sự mang giá trị danh nghĩa hơn với các giá trị rời rạc trong khoảng 1:12.

Ta có thể khắc phục những sự cố này bằng cách nhóm các điểm đến theo tiểu bang và bằng cách chuyển đổi tháng thành một biến danh nghĩa rời rạc thực sự. Vấn đề thứ hai dễ khắc phục hơn, chúng ta chỉ cần sử dụng factor(month)

  1. Tại sao sử dụng aes(x = color, y = cut) lại tốt hơn một chút so với aes(x = cut, y = color) trong ví dụ trên?
diamonds %>%
  group_by(color) %>%
  count(color, cut) %>%
  mutate(
    prop = n/sum(n)
    ) %>%
  ggplot(mapping = aes(x = color, y = cut)) +
  geom_tile(aes(fill = prop))

diamonds %>%
  group_by(color) %>%
  count(color, cut) %>%
  mutate(
    prop = n/sum(n)
    ) %>%
  ggplot(mapping = aes(x = cut, y = color)) +
  geom_tile(aes(fill = prop))

Thông thường, nên đặt biến có nhiều danh mục hơn trên trục x (nằm ngang) vì nó dễ đọc hơn. Ngoài ra, luồng dữ liệu sẽ dễ hiểu hơn khi màu nằm trên trục x. Về cơ bản, đồ họa “thác nước”. Số lượng lớn hơn ở trên cùng và biểu đồ có xu hướng giảm tự nhiên khi nó chảy về góc phần tư phía dưới bên phải. Ngược lại, khi “cắt” nằm trên trục x, cách hiểu về biểu đồ sẽ thay đổi thành nghĩa là khi chúng ta tiếp cận góc phần tư dưới cùng bên phải của biểu đồ, các giá trị sẽ tăng lên - điều này hơi phức tạp hơn để khái niệm hóa.

##5.3 Hai biến liên tục

Ta đã thấy một cách tuyệt vời để trực quan hóa hiệp phương sai giữa hai biến liên tục: vẽ biểu đồ phân tán bằng \(geom\_point()\) . Ta có thể xem sự đồng biến như một mẫu trong các điểm. Ví dụ: ta có thể thấy mối quan hệ theo cấp số nhân giữa kích thước carat và giá của một viên kim cương.

ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_point()

Biểu đồ phân tán trở nên ít hữu ích hơn khi kích thước tập dữ liệu của ta tăng lên, vì các điểm bắt đầu bị vẽ quá mức và chồng chất thành các vùng có màu đen đồng nhất (như trên). Ta đã thấy một cách để khắc phục sự cố: sử dụng thẩm mỹ alpha để thêm độ trong suốt.

ggplot(data = diamonds) +
  geom_point(mapping = aes(x = carat, y = price), alpha = 1/100)

Nhưng việc sử dụng tính minh bạch có thể là một thách thức đối với các tập dữ liệu rất lớn. Một giải pháp khác là sử dụng bin. Trước đây ta đã sử dụng \(geom\_histogram()\)\(geom\_freqpoly()\) để chuyển thành thùng một chiều. Bây giờ, ta sẽ tìm hiểu cách sử dụng \(geom\_bin2d()\)\(geom\_hex()\) để chuyển thành thùng theo hai chiều.

\(geom\_bin2d()\)\(geom\_hex()\) chia mặt phẳng tọa độ thành các ngăn 2d và sau đó sử dụng màu tô để hiển thị số lượng điểm rơi vào mỗi ngăn. \(geom\_bin2d()\) tạo các thùng hình chữ nhật. \(geom\_hex()\) tạo các thùng lục giác. Ta sẽ cần cài đặt gói hexbin để sử dụng \(geom\_hex()\) .

library(hexbin)
ggplot(data = smaller) +
  geom_bin2d(mapping = aes(x = carat, y = price))

ggplot(data = smaller) +
  geom_hex(mapping = aes(x = carat, y = price))

Một tùy chọn khác là bin một biến liên tục để nó hoạt động giống như một biến phân loại. Sau đó, ta có thể sử dụng một trong các kỹ thuật để hình dung sự kết hợp của biến phân loại và biến liên tục mà ta đã học. Ví dụ, ta có thể bin carat và sau đó cho mỗi nhóm, hiển thị một boxplot:

ggplot(data = smaller, mapping = aes(x = carat, y = price)) +
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))

\(cut\_width(x, width)\) , như được sử dụng ở trên, chia x thành các ngăn có chiều rộng width . Theo mặc định, các ô vuông trông gần giống nhau (ngoại trừ số lượng ngoại lệ (bất kể có bao nhiêu quan sát, vì vậy rất khó để biết rằng mỗi ô vuông tóm tắt một số điểm khác nhau. Một cách để chỉ ra điều đó là làm cho chiều rộng của ô boxplot tỷ lệ với số điểm có varwidth = TRUE .))

ggplot(data = smaller, mapping= aes(x = carat, y = price)) +
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))

ggplot(data = smaller, mapping= aes(x = carat, y = price)) +
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)), varwidth = TRUE)

5.3.1 Bài tập

  1. Thay vì tóm tắt phân phối có điều kiện bằng biểu đồ hình hộp, ta có thể sử dụng đa giác tần số. Ta cần cân nhắc điều gì khi sử dụng \(cut\_width()\) so với \(cut\_number()\) ? Điều đó tác động như thế nào đến việc hình dung phân phối 2d của caratgiá ?

Cả \(cut\_width()\)\(cut\_number()\) đều chia biến thành các nhóm nhưng phân tách các giá trị theo cách khác nhau. \(cut\_width\) ngăn dữ liệu thành x số lượng ngăn có cùng chiều rộng nếu varwidth = TRUE không được chỉ định. \(cut\_number()\) chia dữ liệu thành x số ngăn trong đó mỗi ngăn có cùng số lượng giá trị bên trong.

ggplot(data = smaller, mapping = aes(x = price)) +
  geom_freqpoly(mapping = aes(color = cut_width(carat, .5)))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data = smaller, mapping = aes(x = price)) +
  geom_freqpoly(mapping = aes(color = cut_number(carat, n = 5)))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Khi xem các biểu đồ được tạo bằng cách sử dụng các biểu đồ này, điều quan trọng là phải chú ý đến các thùng. Cả hai sẽ thu thập tất cả dữ liệu, nhưng \(cut\_width\) tốt hơn là cho ta biết apprx. bao nhiêu dưới một đường cong trong khi \(cut\_number\) tốt hơn trong việc cung cấp cho ta ý tưởng về số lượng trong một phạm vi cần thiết để tạo thành đường cong.

Ngoài ra, khi sử dụng màu sắc, hãy nhớ rằng cách tốt nhất là không có nhiều hơn 8 loại vì màu sắc ngày càng trở nên ít rõ ràng hơn sau đó.

  1. Trực quan hóa việc phân phối carat, phân vùng theo giá.
ggplot(data = smaller) +
  geom_bin2d(mapping = aes(x = carat, y = price))

ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_boxplot(aes(y = cut_number(price, 10))) +
  labs(x = "carat", y = "price")

ggplot(data = diamonds, mapping = aes(x = cut_width(price, 2000, boundary = 0), y = carat)) +
  geom_boxplot(varwidth = TRUE) +
  coord_flip() +
  labs(x = "carat", y = "price")

  1. Làm thế nào để phân phối giá của những viên kim cương rất lớn so với những viên kim cương nhỏ Nó có như ta mong đợi hay nó làm ta ngạc nhiên?
ggplot(data = diamonds, mapping = aes(x = cut_width(carat, 1, boundary = 0), y = price)) +
  geom_boxplot(varwidth = TRUE) +
  labs(x = "carat", y = "price")

ggplot(data = diamonds, mapping = aes(x = cut_interval(carat, n = 3), y = price)) +
  geom_boxplot(varwidth = TRUE) +
  labs(x = "carat", y = "price")

ggplot(data = diamonds, mapping = aes(x = cut_number(carat, 5), y = price)) +
  geom_boxplot() +
  labs(x = "carat", y = "price")

Điều đầu tiên cần lưu ý là có nhiều carat “nhỏ hơn” (kích thước \(carat /> 3\)) so với carat “lớn hơn” (kích thước \(carat \> 3\)). Trên thực tế, chỉ có 40 viên kim cương trong bộ dữ liệu chứa 53.940 quan sát của ta có kích thước carat lớn hơn 3. Trên thực tế, khoảng 68% viên kim cương trong bộ dữ liệu của ta có kích thước nhỏ hơn hoặc bằng 1.

Điều này thể hiện rõ nhất qua boxplot sử dụng \(cut\_number()\) . Như ta có thể thấy ô vuông nhỏ nhất có thể đạt được cùng số điểm dữ liệu chỉ bằng cách sử dụng phạm vi [.2, .35] trong khi ô cuối cùng yêu cầu phạm vi [1.13, 5.01] để đạt được cùng số lượng kim cương.

Có một mối quan hệ tích cực rõ ràng giữa kích thước carat và giá cả. Khi một trong hai tăng lên, thì cái kia cũng vậy. Có một số yếu tố khác cần xem xét ngoài kích thước carat, nhưng điều có lẽ thú vị nhất là sự gia tăng đáng kể nhất xảy ra khi tăng một số nguyên theo kích thước carat (0:1, 1:2, 2:3) nhưng hiệu ứng vẫn xuất hiện để sắp xếp giảm dần sau 3. Hiệu ứng này có thể đảo ngược với nhiều điểm dữ liệu hơn, nhưng thật kỳ lạ là không có mức tăng giá mà chúng ta mong đợi. Có lẽ những viên kim cương carat “lớn hơn” này có đường cắt hoặc màu sắc “kém hơn” chăng? Hãy kiểm tra.

diamonds %>%
  filter(carat >= 3) %>%
  select(carat, cut, color, price) %>%
  ggplot(mapping = aes(x = carat, y = price)) +
  geom_point()

diamonds %>%
  filter(carat >= 3) %>%
  select(carat, cut, color, price) %>%
  group_by(cut, color) %>%
  mutate(
    count = n()
  ) %>%
  ungroup() %>%
  mutate(
    prop = count/sum(count)
  ) %>%
  ggplot(mapping = aes(x = cut, y = color)) +
  geom_count()

diamonds %>%
  filter(carat >= 3) %>%
  select(carat, cut, color, price) %>%
  group_by(cut, color) %>%
  mutate(
    count = n()
  ) %>%
  ungroup() %>%
  mutate(
    prop = count/sum(count)
  ) %>%
  ggplot(mapping = aes(x = carat, y = cut_number(price, 5))) +
  geom_boxplot() +
  labs(x = "carat", y = "price")

  1. Kết hợp hai trong số các kỹ thuật mà ta đã học để trực quan hóa sự phân phối kết hợp của vết cắt, ca-ra và giá.
ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_hex() +
  facet_wrap(~cut)

ggplot(data = diamonds, mapping = aes(x = cut_number(carat, 5), y = price)) +
  geom_boxplot(aes(color = cut))

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
  geom_boxplot(aes(color = cut_number(carat, 5)))

ggplot(data = diamonds, mapping = aes(x = cut, y = carat)) +
  geom_boxplot(aes(color = cut_number(price, 5)))

  1. Biểu đồ hai chiều cho thấy các giá trị ngoại lệ không thể nhìn thấy trong biểu đồ một chiều. Ví dụ: một số điểm trong biểu đồ bên dưới có sự kết hợp bất thường giữa các giá trị xy , điều này làm cho các điểm trở nên khác biệt mặc dù giá trị xy của chúng có vẻ bình thường khi được kiểm tra riêng.
ggplot(data = diamonds) +
  geom_point(mapping = aes(x = x, y = y)) +
  coord_cartesian(xlim = c(4,11), ylim = c(4,11))

Tại sao một biểu đồ phân tán hiển thị tốt hơn một biểu đồ được đánh dấu trong trường hợp này?

Biểu đồ phân tán là cách hiển thị tốt hơn cho trường hợp này vì có mối quan hệ chặt chẽ giữa x và y. Nếu chúng ta sắp xếp các ô, các giá trị thuộc về x có thể bị phân loại nhầm thành các giá trị ngoại lệ mặc dù chúng rất phù hợp với mối quan hệ hai biến.

6. Các mẫu mô hình

Các mẫu trong dữ liệu của ta cung cấp manh mối về các mối quan hệ. Nếu tồn tại một mối quan hệ hệ thống giữa hai biến thì nó sẽ xuất hiện dưới dạng một mẫu trong dữ liệu. Nếu Ta phát hiện ra một khuôn mẫu, hãy tự hỏi:

Một biểu đồ phân tán về thời gian phun trào của Old Faithful so với thời gian chờ đợi giữa các lần phun trào cho thấy một mô hình: thời gian chờ đợi lâu hơn có liên quan đến các đợt phun trào lâu hơn. Biểu đồ phân tán cũng hiển thị hai cụm mà ta nhận thấy ở trên.

ggplot(data = faithful) +
  geom_point(mapping = aes(x = eruptions, y = waiting))

Các mẫu cung cấp một trong những công cụ hữu ích nhất cho các nhà khoa học dữ liệu vì chúng tiết lộ sự cộng hưởng. Nếu ta nghĩ về sự thay đổi như một hiện tượng tạo ra sự không chắc chắn, thì hiệp phương sai là một hiện tượng làm giảm nó. Nếu hai biến đồng biến, ta có thể sử dụng các giá trị của một biến để đưa ra dự đoán tốt hơn về các giá trị của biến thứ hai. Nếu hiệp phương sai là do mối quan hệ nhân quả (trường hợp đặc biệt), thì ta có thể sử dụng giá trị của một biến để kiểm soát giá trị của biến thứ hai.

Các mô hình là một công cụ để trích xuất các mẫu từ dữ liệu. Ví dụ, hãy xem xét dữ liệu kim cương. Thật khó để hiểu mối quan hệ giữa vết cắt và giá cả, bởi vì vết cắt và carat, carat và giá cả có liên quan chặt chẽ với nhau. Có thể sử dụng một mô hình để loại bỏ mối quan hệ rất chặt chẽ giữa giá và carat để chúng ta có thể khám phá những điều tinh tế còn sót lại. Đoạn mã sau phù hợp với mô hình dự đoán giá từ carat và sau đó tính toán phần dư (chênh lệch giữa giá trị dự đoán và giá trị thực tế). Phần dư cung cấp cho chúng ta một cái nhìn về giá của viên kim cương, sau khi loại bỏ ảnh hưởng của carat.

library(modelr)

mod <- lm(log(price) ~ log(carat), data = diamonds)

diamonds2 <- diamonds %>%
  add_residuals(mod) %>%
  mutate(resid = exp(resid))

ggplot(data = diamonds2) +
  geom_point(mapping = aes(x = carat, y = resid))

Khi đã loại bỏ mối quan hệ chặt chẽ giữa carat và giá cả, ta có thể thấy những gì ta mong đợi trong mối quan hệ giữa đường cắt và giá cả: so với kích thước của chúng, những viên kim cương chất lượng tốt hơn sẽ đắt hơn.

7. Gọi thư viện ggplot2

Khi chúng ta tiếp tục từ các chương giới thiệu này, chúng ta sẽ chuyển sang cách diễn đạt ngắn gọn hơn về mã ggplot2. Cho đến giờ chúng ta đã nói rất rõ ràng, điều này rất hữu ích khi ta học:

ggplot(data = faithful, mapping = aes(x = eruptions)) +
  geom_freqpoly(binwidth = .25)

Thông thường, một hoặc hai đối số đầu tiên của hàm rất quan trọng nên ta phải thuộc lòng chúng. Hai đối số đầu tiên của ggplot()datamapping và hai đối số đầu tiên của aes()xy . Trong phần còn lại của cuốn sách, sách này sẽ không cung cấp những cái tên đó. Điều đó giúp tiết kiệm việc nhập và bằng cách giảm số lượng bản soạn sẵn, giúp ta dễ dàng xem điểm khác nhau giữa các ô. Đó là một mối quan tâm lập trình thực sự quan trọng mà chúng ta sẽ quay lại trong hàm .

Viết lại biểu đồ trước đó mang lại kết quả chính xác hơn:

ggplot(faithful, aes(eruptions)) +
  geom_freqpoly(binwidth = .25)

Đôi khi, ta sẽ cắt phần cuối của một quy trình chuyển đổi ata thành một biểu đồ. Theo dõi quá trình chuyển đổi từ %>% sang + . Tôi ước quá trình chuyển đổi này không cần thiết nhưng tiếc là ggplot2 đã được tạo trước khi đường ống được phát hiện.

diamonds %>%
  count(cut, clarity) %>%
  ggplot(aes(clarity, cut, fill = n)) +
  geom_tile()

8. Phụ lục: Phần tổng hợp các code R sử dụng

library(ggplot2)
library(dplyr)
library(tidyverse)
library(ggbeeswarm)
knitr::opts_chunk$set(echo = TRUE)
ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut))
diamonds %>%
  count(cut)
ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = carat), binwidth = .5)
diamonds %>%
  count(cut_width(carat, .5))
diamonds %>%
  filter(carat < 3) %>%
  ggplot(mapping = aes(x = carat)) +
  geom_histogram(binwidth = .1)
diamonds %>%
  filter(carat < 3) %>%
  ggplot(mapping = aes(x = carat, color = cut)) +
  geom_freqpoly()
smaller <- diamonds
ggplot(data = smaller, mapping = aes(x = carat)) +
  geom_histogram(binwidth = .01)
    ggplot(data = faithful, mapping = aes(x = eruptions)) +
      geom_histogram()
ggplot(data = diamonds, mapping = aes(x = y)) +
  geom_histogram(binwidth = .5)
ggplot(data = diamonds, mapping = aes(x = y)) +
  geom_histogram() +
  coord_cartesian(ylim = c(0, 50))
unusual <- diamonds %>%
  filter(y < 3 | y > 20) %>%
  select(price, x, y, z) %>%
  arrange(y)

unusual
# Tạo dữ liệu mẫu
set.seed(123)
filtered <- data.frame(x = rnorm(1000), y = rnorm(1000), z = rnorm(1000))

# Chọn các quan sát thỏa mãn điều kiện x > -1 & y < 1 & z > -0.5
filtered <- filter(filtered, x > -1 & y < 1 & z > -0.5)
# Định nghĩa đối tượng filtered và các biến x, y, z
summary(select(filtered, x, y, z))

# Vẽ biểu đồ histogram cho biến "x"
filtered %>%
  filter(x < 10) %>%
  ggplot(mapping = aes(x = x)) +
  geom_histogram(binwidth = .01) +
  scale_x_continuous(breaks = 1:10)

# Vẽ biểu đồ histogram cho biến "y"
filtered %>%
  filter(y < 10) %>%
  ggplot(mapping = aes(x = y)) +
  geom_histogram(binwidth = .01) +
  scale_x_continuous(breaks = 1:10)

# Vẽ biểu đồ histogram cho biến "z"
filtered %>%
  filter(z < 10) %>%
  ggplot(mapping = aes(x = z)) +
  geom_histogram(binwidth = .01) +
  scale_x_continuous(breaks = 1:10)
summary(select(diamonds, price))
sd(diamonds$price)
ggplot(data = diamonds, mapping = aes(x = price)) +
  geom_histogram(binwidth = 1000)
ggplot(data = diamonds, mapping = aes(x = price)) +
  geom_histogram(binwidth = 100)
nrow(filter(diamonds, carat == .99))
nrow(filter(diamonds, carat == 1))
ggplot(data = diamonds, mapping = aes(x = carat)) +
  geom_histogram(binwidth = .1)
diamonds %>%
  filter(carat >= .9 & carat <= 1.1) %>%
  count(carat) %>%
  print(n = Inf)
ggplot(data = diamonds, mapping = aes(x = x)) +
  geom_histogram()
ggplot(data = diamonds, mapping = aes(x = x)) +
  geom_histogram() +
  coord_cartesian(xlim = c(2, 10), ylim = c(0, 5000))
ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = x)) +
  xlim(3, 9) +
  ylim(0, 5000)
diamonds2 <- diamonds %>%
  filter(between(y, 3, 20))
diamonds2 <- diamonds %>%
  mutate(y = ifelse(y < 3 | y > 20, NA, y))
ggplot(data = diamonds2, mapping = aes(x = x, y = y)) +
  geom_point()
ggplot(data = diamonds2, mapping = aes(x = x, y = y)) +
  geom_point(na.rm = TRUE)
library(nycflights13)
data("flights")
flights %>%
  mutate(
    cancelled = is.na(dep_time),
    sched_hour = sched_dep_time %/% 100,
    sched_min = sched_dep_time %% 100,
    sched_dep_time = sched_hour + sched_min / 60
    ) %>%
  ggplot(mapping = aes(x = sched_dep_time)) +
  geom_freqpoly(mapping = aes(color = cancelled, binwidth = 1/4))
diamonds %>%
  mutate(y = ifelse(y < 3 | y > 20, NA, y)) %>%
  ggplot(mapping = aes(x = y)) +
  geom_histogram()
diamonds %>%
  mutate(cut = ifelse(runif(n()) < .1, NA_character_, as.character(cut))) %>%
  ggplot(mapping = aes(x = cut)) +
  geom_bar()
diamonds %>%
  mutate(xyz = ifelse(z == 0, NA, z)) %>%
  select(x,y,z,xyz) %>%
  arrange(z) %>%
  summarise(
    total_xyz = sum(xyz, na.rm = TRUE),
    mean_xyz = mean(xyz, na.rm = TRUE)
  )
ggplot(data = diamonds, mapping = aes(x = price)) +
  geom_freqpoly(mapping = aes(color = cut), binwidth = 500)
ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut))
ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) +
  geom_freqpoly(mapping = aes(color = cut), binwidth = 500)
ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = cut, y = price))
ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
  geom_boxplot()
ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))
ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy)) +
  coord_flip()
library(nycflights13)
library(ggplot2)
flights <- flights %>%
  mutate(cancelled = ifelse(is.na(dep_time), 1, 0))
library(ggplot2)
flights %>%
  group_by(year, month, day) %>%
  summarise(prop_cancelled = mean(cancelled)) %>%
  ggplot() +
  geom_boxplot(mapping = aes(x = reorder(as.character(month), prop_cancelled, FUN = median), y = prop_cancelled))
flights %>%
  group_by(year, month) %>%
  summarise(
    prop_cancelled = mean(cancelled)) %>%
  arrange(prop_cancelled)
flights %>%
  mutate(
    sched_dep_hour = sched_dep_time %/% 100,
    sched_dep_min = sched_dep_time %% 100,
    sched_dep_time = sched_dep_hour + sched_dep_min / 60
  ) %>%
  ggplot() +
  geom_boxplot(mapping = aes(x = cancelled, y = sched_dep_time))
flights %>%
  mutate(
    sched_dep_hour = sched_dep_time %/% 100,
    sched_dep_min = sched_dep_time %% 100,
    sched_dep_time = sched_dep_hour + sched_dep_min / 60
  ) %>%
  ggplot(mapping = aes(x = sched_dep_time, y = ..density..)) +
  geom_freqpoly(mapping = aes(color = cancelled))
colnames(diamonds)
summary(diamonds)
ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = carat, fill = cut), binwidth = .1) +
  coord_cartesian(xlim = c(0, 3))
ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut))
ggplot(data = diamonds, mapping = aes(x = color, y = price)) +
  geom_boxplot()
ggplot(data = diamonds, mapping = aes(x = clarity, y = price)) +
  geom_boxplot()
ggplot(data = diamonds, mapping = aes(x = x, y = price)) +
  geom_point() +
  xlim(c(3, 10))
ggplot(data = diamonds, mapping = aes(x = y, y = price)) +
  geom_point() +
  xlim(c(1, 12))
ggplot(data = diamonds, mapping = aes(x = z, y = price)) +
  geom_point() +
  xlim(c(1, 7.5))
ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_point() +
  geom_smooth()
ggplot(data = diamonds, mapping = aes(x = x, y = carat)) +
  geom_point() +
  geom_smooth(se = FALSE) +
  xlim(c(3, 10))
ggplot(data = diamonds, mapping = aes(x = y, y = carat)) +
  geom_point() +
  geom_smooth(se = FALSE) +
  xlim(c(1, 15))
ggplot(data = diamonds, mapping = aes(x = z, y = carat)) +
  geom_point() +
  geom_smooth(se = FALSE) +
  xlim(c(1, 10))
ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = cut, y = carat))
ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = cut, y = carat)) +
  coord_flip()
library(ggplot2)
ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = carat, y = cut))
library(lvplot)
library(ggplot2)
ggplot(data = diamonds) +
  geom_lv(mapping = aes(x = cut, y = price))
ggplot(data = diamonds) +
  geom_violin(mapping = aes(x = price, y = cut))
ggplot(data = diamonds) +
  geom_freqpoly(mapping = aes(x = price, y = ..density.., color = cut))
ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = price)) +
  facet_wrap(~cut, nrow = 5, ncol = 1)
ggplot(data = diamonds) +
  geom_count(mapping = aes(x = cut, y = color))
library(dplyr)
diamonds %>%
  group_by(cut, color) %>%
  summarise(
    cut_by_color = n()
  )
diamonds %>%
  count(color, cut) %>%
  ggplot() +
  geom_tile(mapping = aes(x = color, y = cut, fill = n))
diamonds %>%
  group_by(color) %>%
  count(color, cut) %>%
  mutate(
    prop = n/sum(n)
    ) %>%
  ggplot(mapping = aes(x = color, y = cut)) +
  geom_tile(aes(fill = prop))
diamonds %>%
  group_by(cut) %>%
  count(cut, color) %>%
  mutate(
    prop = n/sum(n)
  ) %>%
  ggplot(mapping = aes(x = color, y = cut)) +
  geom_tile(aes(fill = prop))
library(nycflights13)
flights %>%
  filter(!is.na(arr_delay), arr_delay > 0) %>%
  group_by(dest, month) %>%
  mutate(avg_delay = mean(arr_delay)) %>%
  ggplot(mapping = aes(x = factor(month), y = reorder(dest, distance))) +
  geom_tile(mapping = aes(fill = avg_delay)) +
  labs(x = "Month", y = "Destination", fill = "Average Delay")
diamonds %>%
  group_by(color) %>%
  count(color, cut) %>%
  mutate(
    prop = n/sum(n)
    ) %>%
  ggplot(mapping = aes(x = color, y = cut)) +
  geom_tile(aes(fill = prop))

diamonds %>%
  group_by(color) %>%
  count(color, cut) %>%
  mutate(
    prop = n/sum(n)
    ) %>%
  ggplot(mapping = aes(x = cut, y = color)) +
  geom_tile(aes(fill = prop))
ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_point()
ggplot(data = diamonds) +
  geom_point(mapping = aes(x = carat, y = price), alpha = 1/100)
library(hexbin)
ggplot(data = smaller) +
  geom_bin2d(mapping = aes(x = carat, y = price))

ggplot(data = smaller) +
  geom_hex(mapping = aes(x = carat, y = price))
ggplot(data = smaller, mapping = aes(x = carat, y = price)) +
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))
ggplot(data = smaller, mapping= aes(x = carat, y = price)) +
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))

ggplot(data = smaller, mapping= aes(x = carat, y = price)) +
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)), varwidth = TRUE)
ggplot(data = smaller, mapping = aes(x = price)) +
  geom_freqpoly(mapping = aes(color = cut_width(carat, .5)))

ggplot(data = smaller, mapping = aes(x = price)) +
  geom_freqpoly(mapping = aes(color = cut_number(carat, n = 5)))
ggplot(data = smaller) +
  geom_bin2d(mapping = aes(x = carat, y = price))

ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_boxplot(aes(y = cut_number(price, 10))) +
  labs(x = "carat", y = "price")

ggplot(data = diamonds, mapping = aes(x = cut_width(price, 2000, boundary = 0), y = carat)) +
  geom_boxplot(varwidth = TRUE) +
  coord_flip() +
  labs(x = "carat", y = "price")
ggplot(data = diamonds, mapping = aes(x = cut_width(carat, 1, boundary = 0), y = price)) +
  geom_boxplot(varwidth = TRUE) +
  labs(x = "carat", y = "price")

ggplot(data = diamonds, mapping = aes(x = cut_interval(carat, n = 3), y = price)) +
  geom_boxplot(varwidth = TRUE) +
  labs(x = "carat", y = "price")

ggplot(data = diamonds, mapping = aes(x = cut_number(carat, 5), y = price)) +
  geom_boxplot() +
  labs(x = "carat", y = "price")
diamonds %>%
  filter(carat >= 3) %>%
  select(carat, cut, color, price) %>%
  ggplot(mapping = aes(x = carat, y = price)) +
  geom_point()

diamonds %>%
  filter(carat >= 3) %>%
  select(carat, cut, color, price) %>%
  group_by(cut, color) %>%
  mutate(
    count = n()
  ) %>%
  ungroup() %>%
  mutate(
    prop = count/sum(count)
  ) %>%
  ggplot(mapping = aes(x = cut, y = color)) +
  geom_count()

diamonds %>%
  filter(carat >= 3) %>%
  select(carat, cut, color, price) %>%
  group_by(cut, color) %>%
  mutate(
    count = n()
  ) %>%
  ungroup() %>%
  mutate(
    prop = count/sum(count)
  ) %>%
  ggplot(mapping = aes(x = carat, y = cut_number(price, 5))) +
  geom_boxplot() +
  labs(x = "carat", y = "price")
ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_hex() +
  facet_wrap(~cut)

ggplot(data = diamonds, mapping = aes(x = cut_number(carat, 5), y = price)) +
  geom_boxplot(aes(color = cut))

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
  geom_boxplot(aes(color = cut_number(carat, 5)))

ggplot(data = diamonds, mapping = aes(x = cut, y = carat)) +
  geom_boxplot(aes(color = cut_number(price, 5)))
ggplot(data = diamonds) +
  geom_point(mapping = aes(x = x, y = y)) +
  coord_cartesian(xlim = c(4,11), ylim = c(4,11))
ggplot(data = faithful) +
  geom_point(mapping = aes(x = eruptions, y = waiting))
library(modelr)

mod <- lm(log(price) ~ log(carat), data = diamonds)

diamonds2 <- diamonds %>%
  add_residuals(mod) %>%
  mutate(resid = exp(resid))

ggplot(data = diamonds2) +
  geom_point(mapping = aes(x = carat, y = resid))
ggplot(data = faithful, mapping = aes(x = eruptions)) +
  geom_freqpoly(binwidth = .25)
ggplot(faithful, aes(eruptions)) +
  geom_freqpoly(binwidth = .25)
diamonds %>%
  count(cut, clarity) %>%
  ggplot(aes(clarity, cut, fill = n)) +
  geom_tile()