Sẽ có lúc bạn cần chuyển dữ liệu ở định dạng dài (bạn gọi là "long-form" và ý nghĩa sẽ rõ ở phần sau) sang định dạng rộng và ngược lại. Dữ liệu trong thực tế có đủ mọi định dạng, nhưng hầu hết các hàm trong R được thiết kế cho một dạng cụ thể.
Chẳng hạn, lme4::lmer() cho mô hình tuyến tính hỗn hợp, ggplot2::ggplot() để trực quan hóa được thiết kế cho long-form. Nhưng cũng có những hàm được thiết kế cho wide-form như mosaicplot(). Điều đó có nghĩa là bạn cần chuyển đổi định dạng để phân tích hoặc trực quan hóa trừ khi dữ liệu đã được xử lý sẵn và ở đúng định dạng.
Có một số lựa chọn trong R để chuyển đổi định dạng nhưng có thể khá rối khi bạn mới bắt đầu.
Hướng dẫn này sẽ giúp bạn hiểu cơ chế chuyển đổi dữ liệu giữa long-form và wide-form. Bạn sẽ khám phá các chủ đề sau:
Bạn sẽ hiểu rõ hơn về định dạng dài.
Sau đó, bạn sẽ làm quen với định dạng rộng.
Bạn sẽ điểm lại sự khác nhau giữa hai định dạng dài và rộng;
Tiếp theo, bạn sẽ thấy các bước cần thực hiện để chuyển từ định dạng này sang định dạng kia.
Cuối cùng, bạn sẽ triển khai tất cả bằng R. Bạn sẽ học cách đi từ định dạng dài sang định dạng rộng với các hàm melt() và dcast().
Dữ liệu
Bảng 01
name
gender
year2011
year2012
year2013
year2014
year2015
Jackson Smith
M
74.69
84.99
91.73
105.11
111.04
Sophia Johnson
F
NA
NA
NA
NA
75.89
Emma Williams
F
NA
NA
75.74
86.50
91.50
Aiden Jones
M
NA
NA
NA
71.89
81.42
Liam Brown
M
88.24
96.91
101.85
108.13
112.45
Lucas Davis
M
70.60
83.78
94.17
100.03
106.35
Olivia Miller
F
64.78
80.76
87.30
97.13
103.80
Ava Wilson
F
88.77
96.45
104.72
112.84
NA
Bảng này cho thấy chiều cao của trẻ sơ sinh ở một khu vực nhỏ. Chiều cao được đo từ năm 2011 đến năm 2015. Như bạn có thể đã biết, NA nghĩa là ‘Không có sẵn’. Từ vị trí các NA, bạn có thể suy ra độ tuổi của các bé, nhưng tuổi chính xác thì không có trong bảng. Bảng trông có vẻ gọn gàng, nhưng với bất kỳ ai muốn nghiên cứu sự phát triển của trẻ, có một vấn đề: thông tin quan trọng không phải là thời điểm tuyệt đối khi một bé được đo mà là độ tuổi của bé khi được đo. Bạn có thể chèn thông tin này vào bảng không? Dưới đây là một cách:
Bảng 02
name
gender
age0
age1
age2
age3
age4
age5
age6
Jackson Smith
M
NA
76.69
84.91
97.06
105.73
107.32
NA
Sophia Johnson
F
NA
76.05
NA
NA
NA
NA
NA
Emma Williams
F
72.65
82.46
91.76
NA
NA
NA
NA
Aiden Jones
M
69.76
79.89
NA
NA
NA
NA
NA
Liam Brown
M
NA
NA
85.34
93.22
105.78
105.84
114.82
Lucas Davis
M
71.59
84.87
92.10
100.73
104.21
NA
NA
Olivia Miller
F
62.74
81.39
92.02
98.32
106.44
NA
NA
Ava Wilson
F
NA
NA
88.39
100.67
107.58
111.52
NA
Số lượng NA đã tăng lên. ## Dữ liệu dạng rộng Nhưng bạn sẽ làm gì nếu muốn thể hiện số tháng tuổi của một bé? Sẽ trông như thế này:
Bảng 03
name
gender
m4
m6
m7
m10
m12
...
m55
m60
m65
m72
Jackson Smith
M
NA
NA
NA
NA
76.69
...
NA
107.32
NA
NA
Sophia Johnson
F
NA
NA
NA
NA
76.05
...
NA
NA
NA
NA
Emma Williams
F
NA
NA
NA
72.65
NA
...
NA
NA
NA
NA
Aiden Jones
M
NA
69.76
NA
NA
NA
...
NA
NA
NA
NA
Liam Brown
M
NA
NA
NA
NA
NA
...
NA
105.84
NA
114.82
Lucas Davis
M
NA
NA
71.59
NA
NA
...
104.21
NA
NA
NA
Olivia Miller
F
62.74
NA
NA
NA
NA
...
NA
NA
NA
NA
Ava Wilson
F
NA
NA
NA
NA
NA
...
NA
NA
111.52
NA
Có vô số NA trong bảng trên. Nếu các bé khác nhau về thời điểm đo lường tương đối, wide-form có thể lãng phí rất nhiều chỗ. Một lựa chọn khác là chỉ đưa độ tuổi của các bé khi đến năm 2015, nhưng điều đó chỉ khả thi khi các thời điểm đo tuyệt đối đều giống nhau. Nếu khác nhau theo tháng và bạn muốn biết tuổi theo tháng, cách đó không dùng được. Khi ấy ví dụ này có vẻ là cách duy nhất. Trong trường hợp này, bạn có thể cân nhắc dùng long-form. ## Dữ liệu dạng dài Trong khi tất cả phép đo của một bé hiển thị trên một hàng ở wide-form, ở long-form bạn chỉ thấy một phép đo của một bé trên một hàng. Cách đơn giản nhất để có dạng sau là hiển thị tên trẻ, thời điểm đo và phép đo (chiều cao) trên một hàng. Bảng sau chính là như vậy:
Bảng 04
name
gender
month
height
Olivia Miller
F
4
62.74
Aiden Jones
M
6
69.76
Lucas Davis
M
7
71.59
Emma Williams
F
10
72.65
Jackson Smith
M
12
76.69
...
...
...
...
Liam Brown
M
72
114.82
Nếu bạn sắp xếp dữ liệu theo tên, bạn sẽ có bảng như dưới. Rõ ràng bảng này chiếm ít chỗ hơn Bảng 3 ở trên!
Bảng 05
name
gender
month
height
Jackson Smith
M
12
76.69
Jackson Smith
M
24
84.91
Jackson Smith
M
36
97.06
Jackson Smith
M
48
105.73
Jackson Smith
M
60
107.32
Sophia Johnson
F
12
76.05
Emma Williams
F
10
72.65
...
...
...
...
Ava Wilson
F
65
111.52
Dạng Dài và Dạng Rộng
Lưu ý lại sự khác biệt cơ bản giữa wide-form và long-form: một khác biệt đơn giản là wide-form hiển thị nhiều phép đo của một cá thể trên một hàng và tên cột cho biết các phép đo đó là gì.
Bảng này cho thấy chiều cao của Liam Brown trong các năm 2011-2015 trên một hàng:
Bảng 06
name
gender
year2011
year2012
year2013
year2014
year2015
Liam Brown
M
88.24
96.91
101.85
108.13
112.45
Ở long-form chiều cao của Liam Brown được hiển thị trong một cột:
Bảng 07
name
gender
_
Liam Brown
M
85.34
Liam Brown
M
93.22
Liam Brown
M
105.78
Liam Brown
M
105.84
Liam Brown
M
114.82
Như bạn thấy, bạn không thể biết các phép đo là gì. Nếu viết tên cột, nó sẽ như thế này:
Bảng 08
name
gender
year2011, 2012, 2013, 2014, 2015
Liam Brown
M
85.34
Liam Brown
M
93.22
Liam Brown
M
105.78
Liam Brown
M
105.84
Liam Brown
M
114.82
Nhưng cách này lại có vấn đề riêng. Chiều cao của Sophia Johnson không được đo trong các năm 2011-2014. Nên như dưới đây, bạn khó có thể đặt tên cột một cách dễ dàng:
Bảng 09
name
gender
?
Liam Brown
M
85.34
Liam Brown
M
93.22
Liam Brown
M
105.78
Liam Brown
M
105.84
Liam Brown
M
114.82
Sophia Johnson
F
76.05
Từ Dạng Rộng sang Dạng Dài
Chuyển wide-form sang long-form có thể xem như một quy trình từng bước. Trước khi chuyển các phép đo trên một hàng vào một cột, bạn có thể làm cho bảng chỉ chứa một phép đo trên mỗi hàng. Hãy làm điều đó cho bảng này:
Bảng 10
name
gender
age0
age1
age2
age3
age4
age5
age6
Emma Williams
F
72.65
82.46
91.76
NA
NA
NA
NA
Aiden Jones
M
69.76
79.89
NA
NA
NA
NA
NA
Kết quả như bảng dưới đây:
Bảng 11
name
gender
age0
age1
age2
age3
age4
age5
age6
Emma Williams
F
72.65
NA
NA
NA
NA
Emma Williams
F
82.46
NA
Emma Williams
F
91.76
Aiden Jones
M
69.76
NA
NA
NA
NA
NA
Aiden Jones
M
79.89
Trước khi gom các phép đo đang rải rác qua nhiều cột vào một cột, bạn cần tạo thêm một cột để nói rõ các phép đo đó là gì, như dưới đây:
Bảng 12
name
gender
measure
age0
age1
age2
age3
age4
age5
age6
Emma Williams
F
age0
72.65
NA
NA
NA
NA
Emma Williams
F
age1
82.46
NA
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
NA
NA
NA
NA
NA
Aiden Jones
M
age1
79.89
Tên cột thường là “measure” hoặc “key”. Bạn có thể nhận thấy trong bảng trên có “age0”, “age1”, “age2”, “age3”, “age4”, “age5” xuất hiện cả ở tên cột và trong một cột. Nếu bỏ tên các cột, bảng sẽ như dưới.
Bảng 13
name
gender
measure
_
__
___
____
_____
______
_______
Emma Williams
F
age0
72.65
NA
NA
NA
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
NA
NA
NA
NA
NA
Aiden Jones
M
age1
79.89
Vì mỗi hàng chỉ có một phép đo, bạn có thể gom chúng vào một cột và kết quả sẽ như sau:
Bảng 14
name
gender
measure
Emma Williams
F
age0
72.65
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Hãy đặt tên cột mới cho tất cả các phép đo. Thường được đặt là "value":
Bảng 15
name
gender
measure
value
Emma Williams
F
age0
72.65
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Nhìn vào bảng trên. Bạn có nhận ra từ "age" bị lặp lại không? Bạn có thể lược bỏ phần lặp, sẽ cho kết quả sau:
Bảng 16
name
gender
age
value
Emma Williams
F
0
72.65
Emma Williams
F
1
82.46
Emma Williams
F
2
91.76
Aiden Jones
M
0
69.76
Aiden Jones
M
1
79.89
Age, vốn lặp lại ở mọi hàng, giờ trở thành tên cột. Tên cột value trong bảng trên có thể cụ thể hơn. Bạn thay bằng height, rõ nghĩa hơn. Kết quả như bảng dưới:
Bảng 17
name
gender
age
height
Emma Williams
F
0
72.65
Emma Williams
F
1
82.46
Emma Williams
F
2
91.76
Aiden Jones
M
0
69.76
Aiden Jones
M
1
79.89
Hãy so sánh Bảng 15 với Bảng 17. value, một trong các tên cột của Bảng 15, có thể là bất cứ thứ gì, còn height ở Bảng 17 có nghĩa rất cụ thể: chỉ có thể là chiều cao, trong khi value ở Bảng 15 có thể là bất cứ giá trị nào như chiều cao, cân nặng, tốc độ hoặc màu sắc. Một cách khác như sau:
Bảng 18
name
gender
measure
value
Emma Williams
F
height.age0
72.65
Emma Williams
F
height.age1
82.46
Emma Williams
F
height.age2
91.76
Aiden Jones
M
height.age0
69.76
Aiden Jones
M
height.age1
79.89
Hãy xét tình huống các phép đo đa dạng. Nếu phép đo là chiều cao, cân nặng và BMI, sẽ như bảng dưới. Nhưng trong trường hợp này, cột value có các giá trị số ở thang đo khác nhau nên có thể gây khó hiểu khi đọc. Trong bảng này, chiều cao đo bằng centimet và cân nặng bằng kilogram:
Bảng 19
name
gender
measure
value
Emma Williams
F
height
72.65
Emma Williams
F
weight
8.22
Emma Williams
F
BMI
22.60
Aiden Jones
M
height
69.76
Aiden Jones
M
weight
8.51
Vì vậy khi chuyển wide-form sang long-form, bạn cần quyết định những giá trị nào sẽ được gom vào một cột. Sau đó, bạn có thể quyết định tên cột.
Hãy tóm tắt những gì đã nói đến giờ: để chuyển wide-form sang long-form bạn cần quyết định những cột nào sẽ được chỉnh để đưa vào một cột. Khi làm vậy, nhiều cột được gộp lại thành một cột. Vì thế bạn cần một tên cột cho cột đó, như bạn thấy ở các bảng sau:
Bảng 20
id1
id2
age0
age1
age2
Emma Williams
F
NA
82.46
91.76
Aiden Jones
M
69.76
79.89
NA
Emma Williams
F
NA
82.46
91.76
Bảng 21
id1
id2
age0
age1
age2
Emma Williams
F
NA
Emma Williams
F
82.46
Emma Williams
F
91.76
Aiden Jones
M
69.76
Aiden Jones
M
79.89
Aiden Jones
M
NA
Emma Williams
F
NA
Emma Williams
F
82.46
Emma Williams
F
91.76
Bảng 22
id1
id2
age_
age0
age1
age2
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Aiden Jones
M
age2
NA
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Bảng 23
id1
id2
age_
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Aiden Jones
M
age2
NA
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Bảng 24
id1
id2
key
value
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Aiden Jones
M
age2
NA
Emma Williams
F
age0
NA
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Bảng 25
id1
id2
key
value
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Aiden Jones
M
age0
69.76
Aiden Jones
M
age1
79.89
Emma Williams
F
age1
82.46
Emma Williams
F
age2
91.76
Hãy xem các cột id1 và id2 trong bảng trên. id2 không chỉ một cá nhân mà là thuộc tính (giới tính) của một cá nhân. Nói cách khác, bạn có thể coi nó là một phép đo. Vì vậy bạn có thể chuyển nó sang dạng dài hơn. Dạng dài nhất được tạo thành từ ba cột, đó là id, key và value.
Bảng 26
id
key
value
Emma Williams
gender
F
Emma Williams
age1
82.46
Emma Williams
age2
91.76
Aiden Jones
gender
F
Aiden Jones
age0
69.76
Aiden Jones
age1
79.89
Emma Williams
gender
F
Emma Williams
age1
82.46
Emma Williams
age2
91.76
Bảng trên cho thấy dạng dài nhất từ bảng 25. Nhưng những người khác nhau có thể trùng tên.
Trong trường hợp đó, long-form có thể gây nhầm lẫn vì id không thể xác định một người duy nhất. Bạn có thể thêm một số phía sau tên để phân biệt (ví dụ dùng hàm R make.unique()), nhưng bạn không thể phân biệt tên đã bị chỉnh sửa và tên gốc có kèm số. Hãy nghĩ đến một người thực sự có tên là "John.2"!
Cách rõ ràng là gán một mã số ID khác nhau cho từng người. Khi đó, tên lại trở thành một phép đo khác.
Bảng tiếp theo cho thấy ngay cả tên cũng được coi là phép đo. Theo nghĩa nào đó, đây mới là dạng dài nhất thực sự.
Bảng 27
id
key
value
1
name
Emma Williams
1
gender
F
1
age1
82.46
1
age2
91.76
2
name
Aiden Jones
2
gender
F
2
age0
69.76
2
age1
79.89
3
name
Emma Williams
3
gender
F
3
age1
82.46
3
age2
91.76
Dạng dài nhất là dạng dễ nhất để tạo ra dạng rộng. Nếu bạn đảo ngược quá trình chuyển từ dạng rộng sang dạng dài như ở các bảng 20 đến 25, bạn sẽ thu được dạng rộng. Các bảng dưới đây minh họa quá trình này:
Bảng 28. Các phép đo trong cột key được lặp lại ở tên cột
id
key
name
gender
age0
age1
age2
1
name
Emma Williams
1
gender
F
1
age1
82.46
1
age2
91.76
2
name
Aiden Jones
2
gender
F
2
age0
69.76
2
age1
79.89
3
name
Emma Williams
3
gender
F
3
age1
82.46
3
age2
91.76
Bảng 29. Có thể xóa cột key
id
name
gender
age0
age1
age2
1
Emma Williams
1
F
1
82.46
1
91.76
2
Aiden Jones
2
F
2
69.76
2
79.89
3
Emma Williams
3
F
3
82.46
3
91.76
Bảng 30. Có thể gộp các hàng có cùng id thành một hàng
id
name
gender
age0
age1
age2
1
Emma Williams
F
82.46
91.76
1
1
1
2
Aiden Jones
F
69.76
79.89
2
2
2
3
Emma Williams
F
82.46
91.76
3
3
3
Bảng 31. Xóa mọi hàng không có phép đo nào
id
name
gender
age0
age1
age2
1
Emma Williams
F
82.46
91.76
2
Aiden Jones
F
69.76
79.89
3
Emma Williams
F
82.46
91.76
Yếu tố then chốt khi chuyển đổi từ dạng rộng sang dạng dài là quyết định những cột nào sẽ được gom vào một cột.
Nhưng không nhất thiết chỉ là một cột. Bạn có thể dùng hai cột nếu cần, như trong bảng dưới đây:
Bảng 32
name
gender
h2011
h2012
w2011
w2012
Jackson Smith
M
74.69
84.99
9.60
12.0
Olivia Miller
F
NA
80.76
7.15
10.7
Ava Wilson
F
88.77
96.45
NA
15.0
Bạn có thể chuyển nó sang long-form không?
Các cột h2011, h2012, w2011, w2012 lần lượt là chiều cao và cân nặng trong các năm 2011 và 2012. height và weight được đo trên các thang đo khác nhau - cm, kg. Vì vậy bạn có thể dùng chỉ một cột cho cả chiều cao và cân nặng hoặc dùng hai cột, như trong bảng này:
Bảng 33
name
gender
year
height
weight
Jackson Smith
M
2011
74.69
9.60
Jackson Smith
M
2012
84.99
12.00
Olivia Miller
F
2011
NA
80.76
Olivia Miller
F
2012
7.15
10.70
Ava Wilson
F
2011
88.77
96.45
Ava Wilson
F
2012
NA
15.00
Đây là long-form hay wide-form?
Từ dữ liệu Long sang Wide trong R
Hãy thực hiện việc chuyển đổi giữa long-form và wide-form với R. Có nhiều hàm và gói có sẵn cho tác vụ này.
Bảng 34
func
package
to_long_form
to_wide_form
stack/unstack
utils
stack
unstack
reshape
stats
reshape(direction="long", ...)
reshape(direction="wide", ...)
melt/dcast
reshape2
melt
dcast
gather/spread
tidyr
gather
spread
Tại đây tôi sẽ giải thích cách dùng các hàm melt() và dcast() của gói reshape2. Lý do là stack() và unstack() là các hàm cơ bản nên cần hậu xử lý kết quả từ chúng. Lưu ý rằng gather() và spread() là các hàm của tidyr cũng rất phổ biến, nhưng sẽ không được đề cập trong hướng dẫn này.
Một hàm khác bạn có thể dùng là reshape() thuộc gói stats. Thực tế, đừng nhầm lẫn với hàm này vì còn có một gói tên là reshape! Gói này, cùng với reshape2, được phát triển cho những ai gặp khó khăn với reshape(). Ngoài ra, hàm reshape() dường như giả định dữ liệu là dạng dọc (longitudinal), nghĩa là các phép đo được lặp lại theo thời gian.
Hãy chuyển dữ liệu ở bảng 35 sang long-form. Giả sử bảng dưới lưu một data.frame tên là dat.
## name gender year2011 year2012 year2013
## 1 Jackson Smith M74.6984.9991.73
## 2 Emma Williams FNANA75.74
## 3 Liam Brown M88.24NA101.83
## 4 Ava Wilson F88.7796.45NAĐược hỗ trợ bởi
Câu hỏi bạn cần đặt ra là: "những cột nào cần gộp thành một cột?".
Đó là các cột year2011, year2012, year2013. Khi dùng hàm melt(), các cột này nên được liệt kê cho measure.vars:
melt(babies, measure.vars =c("year2011","year2012","year2013"))
## name gender variable value
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NAĐược hỗ trợ bởi
year2011, year2012, year2013 là các cột nằm ở vị trí thứ 3, 4, 5 nên bạn có thể dùng cách đó:
melt(babies, measure.vars =3:5)
## name gender variable value
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NAĐược hỗ trợ bởi
Kết quả nằm trong bảng phía trên. Lưu ý tên cột variable và value. Bảng 23 và bảng 24 cho thấy các tên cột mới là cần thiết. Hàm melt() có các tên mặc định cho cột mới là variable và value. Bạn có thể ghi đè giá trị mặc định bằng cách dùng đối số variable.name= và value.name=. Hãy làm điều này bên dưới:
melt(babies, measure.vars=3:5, variable.name="year", value.name="height")
## name gender year height
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NAĐược hỗ trợ bởi
Các phép đo đã được chỉ định trong các ví dụ trên. Với các thiết lập bạn đang xử lý, Id có thể gián tiếp chỉ định các phép đo. Vì vậy bạn có thể chọn các cột cho id thay vì cho phép đo.
Kết quả là như nhau cho cả hai cách dưới đây. Cách thứ nhất chọn các cột cho id, cách thứ hai chọn các cột để gom lại:
melt(babies, id.vars=c("name","gender"))melt(babies, measure.vars =c("year2011","year2012","year2013"))
## name gender variable value
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NAĐược hỗ trợ bởi
Nếu bạn chỉ định cả id.vars và measure.vars, các cột không thuộc bất kỳ nhóm nào trong hai nhóm này sẽ bị loại khỏi kết quả. Ví dụ, xem đoạn mã dưới đây:
melt(babies, id.vars=c("name"), measure.vars=c("year2011","year2012"))
## name variable value
## 1 Jackson Smith year2011 74.69
## 2 Emma Williams year2011 NA
## 3 Liam Brown year2011 88.24
## 4 Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 6 Emma Williams year2012 NA
## 7 Liam Brown year2012 NA
## 8 Ava Wilson year2012 96.45Được hỗ trợ bởi
Ngoài ra, na.rm=T có thể loại bỏ các giá trị NA:
melt(babies, id.vars=c("name"), measure.vars=c("year2011","year2012"), na.rm=T)
## name variable value
## 1 Jackson Smith year2011 74.69
## 3 Liam Brown year2011 88.24
## 4 Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 8 Ava Wilson year2012 96.45Được hỗ trợ bởi
Hàm dcast() thực hiện thao tác ngược lại với melt(). id, variable và value là các cột của dạng dài nhất. dcast(data, id ~ variable, value.var="value") chuyển từ dạng dài nhất sang dạng rộng.
babiesLong <-melt(babies, id.vars=c("name"), measure.vars=c("year2011","year2012"), na.rm=T)
babiesLong
## name variable value
## 1 Jackson Smith year2011 74.69
## 3 Liam Brown year2011 88.24
## 4 Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 8 Ava Wilson year2012 96.45Được hỗ trợ bởi
babiesLong có name cho id, variable cho variable, và value cho value. Vì vậy bạn có thể dùng dcast() để đưa nó về dạng rộng:
dcast(babiesLong, name ~ variable, value.var="value")
## name year2011 year2012
## 1 Ava Wilson 88.7796.45
## 2 Jackson Smith 74.6984.99
## 3 Liam Brown 88.24NAĐược hỗ trợ bởi
Kết luận
Vậy là hầu như xong. Trong hướng dẫn này, bạn tập trung vào việc hiểu logic và quy trình tái cấu trúc dữ liệu. Thông thường, dạng dài được ưu tiên cho phân tích nhưng vẫn có ngoại lệ. Trình bày dữ liệu ở dạng rộng có thể tiết kiệm không gian khi phù hợp, nhưng khi số lượng giá trị khuyết tăng lên, bạn nên chuyển sang dạng dài.
Đối với việc chuyển đổi định dạng, bạn cần tập trung vào cái gì là phép đo. Châm ngôn cho dạng dài là mỗi hàng một phép đo, còn dạng rộng có nhiều phép đo trong một hàng. Khi bạn gộp các phép đo trải khắp nhiều cột vào một cột, tên cột gốc cần được giữ lại trong một cột bổ sung. Khi bạn trải các phép đo trong một cột ra nhiều cột, bạn cần xác định tên cột dựa trên cột variable.
Một khi hiểu quy trình, việc dùng các hàm như dcast() và melt() sẽ dễ dàng hơn rất nhiều!
Sẽ có lúc bạn cần chuyển dữ liệu ở định dạng dài (bạn gọi là "long-form" và ý nghĩa sẽ rõ ở phần sau) sang định dạng rộng và ngược lại. Dữ liệu trong thực tế có đủ mọi định dạng, nhưng hầu hết các hàm trong R được thiết kế cho một dạng cụ thể.
Chẳng hạn,
lme4::lmer()cho mô hình tuyến tính hỗn hợp,ggplot2::ggplot()để trực quan hóa được thiết kế cho long-form. Nhưng cũng có những hàm được thiết kế cho wide-form nhưmosaicplot(). Điều đó có nghĩa là bạn cần chuyển đổi định dạng để phân tích hoặc trực quan hóa trừ khi dữ liệu đã được xử lý sẵn và ở đúng định dạng.Có một số lựa chọn trong R để chuyển đổi định dạng nhưng có thể khá rối khi bạn mới bắt đầu.
Hướng dẫn này sẽ giúp bạn hiểu cơ chế chuyển đổi dữ liệu giữa long-form và wide-form. Bạn sẽ khám phá các chủ đề sau:
melt()vàdcast().Dữ liệu
Bảng này cho thấy chiều cao của trẻ sơ sinh ở một khu vực nhỏ. Chiều cao được đo từ năm 2011 đến năm 2015. Như bạn có thể đã biết,
NAnghĩa là ‘Không có sẵn’. Từ vị trí các NA, bạn có thể suy ra độ tuổi của các bé, nhưng tuổi chính xác thì không có trong bảng. Bảng trông có vẻ gọn gàng, nhưng với bất kỳ ai muốn nghiên cứu sự phát triển của trẻ, có một vấn đề: thông tin quan trọng không phải là thời điểm tuyệt đối khi một bé được đo mà là độ tuổi của bé khi được đo. Bạn có thể chèn thông tin này vào bảng không? Dưới đây là một cách:Số lượng
NAđã tăng lên. ## Dữ liệu dạng rộng Nhưng bạn sẽ làm gì nếu muốn thể hiện số tháng tuổi của một bé? Sẽ trông như thế này:Có vô số
NAtrong bảng trên. Nếu các bé khác nhau về thời điểm đo lường tương đối, wide-form có thể lãng phí rất nhiều chỗ. Một lựa chọn khác là chỉ đưa độ tuổi của các bé khi đến năm 2015, nhưng điều đó chỉ khả thi khi các thời điểm đo tuyệt đối đều giống nhau. Nếu khác nhau theo tháng và bạn muốn biết tuổi theo tháng, cách đó không dùng được. Khi ấy ví dụ này có vẻ là cách duy nhất. Trong trường hợp này, bạn có thể cân nhắc dùng long-form. ## Dữ liệu dạng dài Trong khi tất cả phép đo của một bé hiển thị trên một hàng ở wide-form, ở long-form bạn chỉ thấy một phép đo của một bé trên một hàng. Cách đơn giản nhất để có dạng sau là hiển thị tên trẻ, thời điểm đo và phép đo (chiều cao) trên một hàng. Bảng sau chính là như vậy:Nếu bạn sắp xếp dữ liệu theo tên, bạn sẽ có bảng như dưới. Rõ ràng bảng này chiếm ít chỗ hơn Bảng 3 ở trên!
Dạng Dài và Dạng Rộng
Lưu ý lại sự khác biệt cơ bản giữa wide-form và long-form: một khác biệt đơn giản là wide-form hiển thị nhiều phép đo của một cá thể trên một hàng và tên cột cho biết các phép đo đó là gì.
Bảng này cho thấy chiều cao của Liam Brown trong các năm 2011-2015 trên một hàng:
Ở long-form chiều cao của Liam Brown được hiển thị trong một cột:
Như bạn thấy, bạn không thể biết các phép đo là gì. Nếu viết tên cột, nó sẽ như thế này:
Nhưng cách này lại có vấn đề riêng. Chiều cao của Sophia Johnson không được đo trong các năm 2011-2014. Nên như dưới đây, bạn khó có thể đặt tên cột một cách dễ dàng:
Từ Dạng Rộng sang Dạng Dài
Chuyển wide-form sang long-form có thể xem như một quy trình từng bước. Trước khi chuyển các phép đo trên một hàng vào một cột, bạn có thể làm cho bảng chỉ chứa một phép đo trên mỗi hàng. Hãy làm điều đó cho bảng này:
Kết quả như bảng dưới đây:
Trước khi gom các phép đo đang rải rác qua nhiều cột vào một cột, bạn cần tạo thêm một cột để nói rõ các phép đo đó là gì, như dưới đây:
Tên cột thường là
“measure”hoặc“key”. Bạn có thể nhận thấy trong bảng trên có“age0”,“age1”,“age2”,“age3”,“age4”,“age5”xuất hiện cả ở tên cột và trong một cột. Nếu bỏ tên các cột, bảng sẽ như dưới.Vì mỗi hàng chỉ có một phép đo, bạn có thể gom chúng vào một cột và kết quả sẽ như sau:
Hãy đặt tên cột mới cho tất cả các phép đo. Thường được đặt là "value":
Nhìn vào bảng trên. Bạn có nhận ra từ "age" bị lặp lại không? Bạn có thể lược bỏ phần lặp, sẽ cho kết quả sau:
Age, vốn lặp lại ở mọi hàng, giờ trở thành tên cột. Tên cộtvaluetrong bảng trên có thể cụ thể hơn. Bạn thay bằngheight, rõ nghĩa hơn. Kết quả như bảng dưới:Hãy so sánh Bảng 15 với Bảng 17.
value, một trong các tên cột của Bảng 15, có thể là bất cứ thứ gì, cònheightở Bảng 17 có nghĩa rất cụ thể: chỉ có thể là chiều cao, trong khi value ở Bảng 15 có thể là bất cứ giá trị nào như chiều cao, cân nặng, tốc độ hoặc màu sắc. Một cách khác như sau:Hãy xét tình huống các phép đo đa dạng. Nếu phép đo là chiều cao, cân nặng và BMI, sẽ như bảng dưới. Nhưng trong trường hợp này, cột
valuecó các giá trị số ở thang đo khác nhau nên có thể gây khó hiểu khi đọc. Trong bảng này, chiều cao đo bằng centimet và cân nặng bằng kilogram:Vì vậy khi chuyển wide-form sang long-form, bạn cần quyết định những giá trị nào sẽ được gom vào một cột. Sau đó, bạn có thể quyết định tên cột.
Hãy tóm tắt những gì đã nói đến giờ: để chuyển wide-form sang long-form bạn cần quyết định những cột nào sẽ được chỉnh để đưa vào một cột. Khi làm vậy, nhiều cột được gộp lại thành một cột. Vì thế bạn cần một tên cột cho cột đó, như bạn thấy ở các bảng sau:
Hãy xem các cột
id1vàid2trong bảng trên.id2không chỉ một cá nhân mà là thuộc tính (giới tính) của một cá nhân. Nói cách khác, bạn có thể coi nó là một phép đo. Vì vậy bạn có thể chuyển nó sang dạng dài hơn. Dạng dài nhất được tạo thành từ ba cột, đó làid,keyvàvalue.Bảng trên cho thấy dạng dài nhất từ bảng 25. Nhưng những người khác nhau có thể trùng tên.
Trong trường hợp đó, long-form có thể gây nhầm lẫn vì
idkhông thể xác định một người duy nhất. Bạn có thể thêm một số phía sau tên để phân biệt (ví dụ dùng hàm Rmake.unique()), nhưng bạn không thể phân biệt tên đã bị chỉnh sửa và tên gốc có kèm số. Hãy nghĩ đến một người thực sự có tên là "John.2"!Cách rõ ràng là gán một mã số ID khác nhau cho từng người. Khi đó, tên lại trở thành một phép đo khác.
Bảng tiếp theo cho thấy ngay cả tên cũng được coi là phép đo. Theo nghĩa nào đó, đây mới là dạng dài nhất thực sự.
Dạng dài nhất là dạng dễ nhất để tạo ra dạng rộng. Nếu bạn đảo ngược quá trình chuyển từ dạng rộng sang dạng dài như ở các bảng 20 đến 25, bạn sẽ thu được dạng rộng. Các bảng dưới đây minh họa quá trình này:
Yếu tố then chốt khi chuyển đổi từ dạng rộng sang dạng dài là quyết định những cột nào sẽ được gom vào một cột.
Nhưng không nhất thiết chỉ là một cột. Bạn có thể dùng hai cột nếu cần, như trong bảng dưới đây:
Bạn có thể chuyển nó sang long-form không?
Các cột
h2011,h2012,w2011,w2012lần lượt là chiều cao và cân nặng trong các năm 2011 và 2012.heightvàweightđược đo trên các thang đo khác nhau - cm, kg. Vì vậy bạn có thể dùng chỉ một cột cho cả chiều cao và cân nặng hoặc dùng hai cột, như trong bảng này:Đây là long-form hay wide-form?
Từ dữ liệu Long sang Wide trong R
Hãy thực hiện việc chuyển đổi giữa long-form và wide-form với R. Có nhiều hàm và gói có sẵn cho tác vụ này.
Tại đây tôi sẽ giải thích cách dùng các hàm
melt()vàdcast()của góireshape2. Lý do làstack()vàunstack()là các hàm cơ bản nên cần hậu xử lý kết quả từ chúng. Lưu ý rằnggather()vàspread()là các hàm củatidyrcũng rất phổ biến, nhưng sẽ không được đề cập trong hướng dẫn này.Một hàm khác bạn có thể dùng là
reshape()thuộc góistats. Thực tế, đừng nhầm lẫn với hàm này vì còn có một gói tên làreshape! Gói này, cùng vớireshape2, được phát triển cho những ai gặp khó khăn vớireshape(). Ngoài ra, hàmreshape()dường như giả định dữ liệu là dạng dọc (longitudinal), nghĩa là các phép đo được lặp lại theo thời gian.Hãy chuyển dữ liệu ở bảng 35 sang long-form. Giả sử bảng dưới lưu một
data.frametên làdat.babies = data.frame(name= c("Jackson Smith", "Emma Williams", "Liam Brown", "Ava Wilson"), gender = c("M", "F", "M", "F"), year2011= c(74.69, NA, 88.24, 88.77), year2012=c(84.99, NA, NA, 96.45), year2013=c(91.73, 75.74, 101.83, NA)) babies
Câu hỏi bạn cần đặt ra là: "những cột nào cần gộp thành một cột?".
Đó là các cột
year2011,year2012,year2013. Khi dùng hàmmelt(), các cột này nên được liệt kê chomeasure.vars:year2011,year2012,year2013là các cột nằm ở vị trí thứ 3, 4, 5 nên bạn có thể dùng cách đó:Kết quả nằm trong bảng phía trên. Lưu ý tên cột
variablevàvalue. Bảng 23 và bảng 24 cho thấy các tên cột mới là cần thiết. Hàmmelt()có các tên mặc định cho cột mới làvariablevàvalue. Bạn có thể ghi đè giá trị mặc định bằng cách dùng đối sốvariable.name=vàvalue.name=. Hãy làm điều này bên dưới:Các phép đo đã được chỉ định trong các ví dụ trên. Với các thiết lập bạn đang xử lý,
Idcó thể gián tiếp chỉ định các phép đo. Vì vậy bạn có thể chọn các cột cho id thay vì cho phép đo.Kết quả là như nhau cho cả hai cách dưới đây. Cách thứ nhất chọn các cột cho id, cách thứ hai chọn các cột để gom lại:
Nếu bạn chỉ định cả
id.varsvàmeasure.vars, các cột không thuộc bất kỳ nhóm nào trong hai nhóm này sẽ bị loại khỏi kết quả. Ví dụ, xem đoạn mã dưới đây:Ngoài ra,
na.rm=Tcó thể loại bỏ các giá trị NA:Hàm
dcast()thực hiện thao tác ngược lại vớimelt().id,variablevàvaluelà các cột của dạng dài nhất.dcast(data, id ~ variable, value.var="value")chuyển từ dạng dài nhất sang dạng rộng.babiesLongcó name choid, variable chovariable, và value chovalue. Vì vậy bạn có thể dùngdcast()để đưa nó về dạng rộng:Kết luận
Vậy là hầu như xong. Trong hướng dẫn này, bạn tập trung vào việc hiểu logic và quy trình tái cấu trúc dữ liệu. Thông thường, dạng dài được ưu tiên cho phân tích nhưng vẫn có ngoại lệ. Trình bày dữ liệu ở dạng rộng có thể tiết kiệm không gian khi phù hợp, nhưng khi số lượng giá trị khuyết tăng lên, bạn nên chuyển sang dạng dài.
Đối với việc chuyển đổi định dạng, bạn cần tập trung vào cái gì là phép đo. Châm ngôn cho dạng dài là mỗi hàng một phép đo, còn dạng rộng có nhiều phép đo trong một hàng. Khi bạn gộp các phép đo trải khắp nhiều cột vào một cột, tên cột gốc cần được giữ lại trong một cột bổ sung. Khi bạn trải các phép đo trong một cột ra nhiều cột, bạn cần xác định tên cột dựa trên cột
variable.Một khi hiểu quy trình, việc dùng các hàm như
dcast()vàmelt()sẽ dễ dàng hơn rất nhiều!