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 :
Tạo câu hỏi về dữ liệu.
Tìm kiếm câu trả lời bằng cách trực quan hóa, biến đổi và lập mô hình dữ liệu.
Sử dụng những gì đã học để tinh chỉnh câu hỏi của mình và hoặc tạo câu hỏi mới.
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.
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ư:
Loại biến thể nào xảy ra trong các biến của tôi?
Loại hiệp phương sai nào xảy ra giữa các biến của tôi?
Phần còn lại của chương này sẽ xem xét hai câu hỏi này.
Biến số là số lượng, chất lượng hoặc thuộc tính mà ta có thể đo lường.
Một giá trị là trạng thái của một biến khi ta đo lường nó. Giá trị của một biến có thể thay đổi từ phép đo này sang phép đo khác.
Một quan sát là một tập hợp các phép đo được thực hiện trong các điều kiện tương tự (ta thường thực hiện tất cả các phép đo trong một lần quan sát cùng một lúc và trên cùng một đối tượng). Một quan sát sẽ chứa một số giá trị, mỗi giá trị được liên kết với một biến khác nhau. Đôi khi tôi sẽ coi một quan sát là một điểm dữ liệu.
Dữ liệu dạng bảng là một tập hợp các giá trị, mỗi giá trị được liên kết với một biến và một quan sát. Dữ liệu dạng bảng sẽ gọn gàng hơn nếu giá trị eavh được đặt trong “ô” riêng của nó, mỗi biến trong cột riêng và mỗi quan sát trong hàng riêng.
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.
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?).
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`.
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
# 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)
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.
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
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()`).
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.
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!
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, có , khi kiểm tra là TRUE 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`.
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()
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.
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.
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()
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.
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.
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.
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.
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.
Hai \(geom\_beeswarm\) và \(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
Để 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))
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")
Một vài điều làm cho biểu đồ khó đọc.
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)
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()\) và \(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()\) và \(geom\_hex()\) để chuyển thành thùng theo hai chiều.
\(geom\_bin2d()\) và \(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)
Cả \(cut\_width()\) và \(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 đó.
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")
Đ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")
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))
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.
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ô hình này có thể là do trùng hợp ngẫu nhiên (tức là cơ hội ngẫu nhiên)?
Làm thế nào ta có thể mô tả mối quan hệ ngụ ý bởi các mẫu?
Làm thế nào mạnh mẽ là mối quan hệ ngụ ý của mô hình?
Những biến nào khác có thể ảnh hưởng đến mối quan hệ?
Mối quan hệ có thay đổi nếu ta xem xét các nhóm con riêng lẻ của dữ liệu không?
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.
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() là data và mapping và hai đối số đầu tiên của aes() là x và y . 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()
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()