25  Kỹ thuật reshape dữ liệu

25.1 Sử dụng lệnh reshape của base R

Lệnh stats::reshape này là căn bản của base R giúp chuyển dữ liệu từ long sang wide và ngược lại. Hạn chế là nếu dữ liệu unbalanced thì quá trình chuyển đổi sẽ phức tạp.

Lưu ý là khái niệm semi-longsemi-wide data là nói về các dạng dữ liệu ở trạng thái longwide chưa triệt để. Còn longwide nói về dạng dữ liệu ở trạng thái longwide triệt để. Như vậy cùng 1 bộ dataset khi reshape sẽ có nhiều hơn 1 cách sắp xếp dữ liệu tùy vào nhu cầu phân tích của người dùng (chủ yếu để phù hợp với đặc điểm dữ liệu mà các function trong R yêu cầu khi vẽ đồ thị hay phân tích thống kê).

25.1.1 Áp dụng lệnh reshape của base R cho imbalanced dataset

Ta có dataset ở dạng long, ký hiệu là survey với thông tin như sau. Trong đây thì cột id là mã số người tham gia khảo sát, gender là giới tính, age là tuổi, income là thu nhập và year là năm.

id <- c("111", "111", "112", "112", "112", 
        "114", "315", "315", "315", "539")

gender <- c("male", "male", "female", "female", "female", 
            "female", "male", "male", "male", "female")

age <- c(56, 58, 29, 31, 33, 
         NA, 86, 88, 90, NA)

income <- c(1000, 2500, NA, 400, 540, 
            200, 440, NA, NA, 300)

year <- c(2012, 2014, 2012, 2014, 2016,
          2016, 2012, 2014, 2016, 2016)

survey <- data.frame(id, gender, age, income, year)

survey
    id gender age income year
1  111   male  56   1000 2012
2  111   male  58   2500 2014
3  112 female  29     NA 2012
4  112 female  31    400 2014
5  112 female  33    540 2016
6  114 female  NA    200 2016
7  315   male  86    440 2012
8  315   male  88     NA 2014
9  315   male  90     NA 2016
10 539 female  NA    300 2016

idgender không thay đổi qua các năm ở cùng 1 cá nhân nên khi reshape từ long sang wide ta sẽ giữ lại 2 cột này.

survey_wide_1 <- reshape(survey, 
                         direction = "wide", # định vị kiểu spread dữ liệu dạng wide
                         idvar = c("id", "gender"), # giữ lại 2 cột này
                         timevar = "year", # spread ra theo cột `year`
                         v.names = c("age", "income") # chọn 2 cột để spread dữ liệu
                         ) 

survey_wide_1 # dữ liệu sau khi reshape
    id gender age.2012 income.2012 age.2014 income.2014 age.2016 income.2016
1  111   male       56        1000       58        2500       NA          NA
3  112 female       29          NA       31         400       33         540
6  114 female       NA          NA       NA          NA       NA         200
7  315   male       86         440       88          NA       90          NA
10 539 female       NA          NA       NA          NA       NA         300
row.names(survey_wide_1) <- NULL

attributes(survey_wide_1)$reshapeWide <- NULL

# dataset đã làm sạch attributes 
# có thể sắp xếp thứ tự cột cho chuẩn về 1 trật tự thuận tiện cho việc subset sau này
survey_wide_1 
   id gender age.2012 income.2012 age.2014 income.2014 age.2016 income.2016
1 111   male       56        1000       58        2500       NA          NA
2 112 female       29          NA       31         400       33         540
3 114 female       NA          NA       NA          NA       NA         200
4 315   male       86         440       88          NA       90          NA
5 539 female       NA          NA       NA          NA       NA         300

Như vậy ta có dữ liệu ở dạng wide, giờ nếu chuyển về dạng long thì R sẽ chuyển về dạng long triệt để, rồi sau đó ta mới tìm cách chuyển về dạng semi-long như là dataset survey ban đầu.

survey_long_1 <- reshape(survey_wide_1, 
                         direction = "long", # định vị kiểu spread dữ liệu dạng long
                         varying = list(3:8), # xác định các cột được rút lại khi reshape về dạng long
                         v.names = "value", # tên cột mới chứa giá trị tương ứng
                         timevar = "variable", # tên cột mới chứa tên của các cột được rút lại theo varying
                         times = names(survey_wide_1)[3:8], # R căn cứ vào đây để đưa tên cột `variable`
                         idvar = c("id") # nếu trong dataset ban đầu có cột id thì chỉ định luôn, 
                         #nếu không thì ta cần đặt tên cột `sample_id` 
                         #để R chỉ ra vị trí các cột sau khi gather
                      )

row.names(survey_long_1) <- NULL

attributes(survey_long_1)$reshapeLong <- NULL

survey_long_1
    id gender    variable value
1  111   male    age.2012    56
2  112 female    age.2012    29
3  114 female    age.2012    NA
4  315   male    age.2012    86
5  539 female    age.2012    NA
6  111   male income.2012  1000
7  112 female income.2012    NA
8  114 female income.2012    NA
9  315   male income.2012   440
10 539 female income.2012    NA
11 111   male    age.2014    58
12 112 female    age.2014    31
13 114 female    age.2014    NA
14 315   male    age.2014    88
15 539 female    age.2014    NA
16 111   male income.2014  2500
17 112 female income.2014   400
18 114 female income.2014    NA
19 315   male income.2014    NA
20 539 female income.2014    NA
21 111   male    age.2016    NA
22 112 female    age.2016    33
23 114 female    age.2016    NA
24 315   male    age.2016    90
25 539 female    age.2016    NA
26 111   male income.2016    NA
27 112 female income.2016   540
28 114 female income.2016   200
29 315   male income.2016    NA
30 539 female income.2016   300

Tiếp tục chuyển survey_long_1 về dạng cấu trúc y chang dataset survey ban đầu bằng cách reshape qua dạng wide cho các biến ageincome.

Bước 1: Tách ra thành các cột riêng

strsplit(survey_long_1$variable, split = "\\.") -> ok

ok
[[1]]
[1] "age"  "2012"

[[2]]
[1] "age"  "2012"

[[3]]
[1] "age"  "2012"

[[4]]
[1] "age"  "2012"

[[5]]
[1] "age"  "2012"

[[6]]
[1] "income" "2012"  

[[7]]
[1] "income" "2012"  

[[8]]
[1] "income" "2012"  

[[9]]
[1] "income" "2012"  

[[10]]
[1] "income" "2012"  

[[11]]
[1] "age"  "2014"

[[12]]
[1] "age"  "2014"

[[13]]
[1] "age"  "2014"

[[14]]
[1] "age"  "2014"

[[15]]
[1] "age"  "2014"

[[16]]
[1] "income" "2014"  

[[17]]
[1] "income" "2014"  

[[18]]
[1] "income" "2014"  

[[19]]
[1] "income" "2014"  

[[20]]
[1] "income" "2014"  

[[21]]
[1] "age"  "2016"

[[22]]
[1] "age"  "2016"

[[23]]
[1] "age"  "2016"

[[24]]
[1] "age"  "2016"

[[25]]
[1] "age"  "2016"

[[26]]
[1] "income" "2016"  

[[27]]
[1] "income" "2016"  

[[28]]
[1] "income" "2016"  

[[29]]
[1] "income" "2016"  

[[30]]
[1] "income" "2016"  
do.call(rbind, ok) -> ok_1

ok_1
      [,1]     [,2]  
 [1,] "age"    "2012"
 [2,] "age"    "2012"
 [3,] "age"    "2012"
 [4,] "age"    "2012"
 [5,] "age"    "2012"
 [6,] "income" "2012"
 [7,] "income" "2012"
 [8,] "income" "2012"
 [9,] "income" "2012"
[10,] "income" "2012"
[11,] "age"    "2014"
[12,] "age"    "2014"
[13,] "age"    "2014"
[14,] "age"    "2014"
[15,] "age"    "2014"
[16,] "income" "2014"
[17,] "income" "2014"
[18,] "income" "2014"
[19,] "income" "2014"
[20,] "income" "2014"
[21,] "age"    "2016"
[22,] "age"    "2016"
[23,] "age"    "2016"
[24,] "age"    "2016"
[25,] "age"    "2016"
[26,] "income" "2016"
[27,] "income" "2016"
[28,] "income" "2016"
[29,] "income" "2016"
[30,] "income" "2016"
survey_long_1$chi_tieu_theo_doi <- ok_1[, 1]

survey_long_1$year <- ok_1[, 2]

survey_long_1
    id gender    variable value chi_tieu_theo_doi year
1  111   male    age.2012    56               age 2012
2  112 female    age.2012    29               age 2012
3  114 female    age.2012    NA               age 2012
4  315   male    age.2012    86               age 2012
5  539 female    age.2012    NA               age 2012
6  111   male income.2012  1000            income 2012
7  112 female income.2012    NA            income 2012
8  114 female income.2012    NA            income 2012
9  315   male income.2012   440            income 2012
10 539 female income.2012    NA            income 2012
11 111   male    age.2014    58               age 2014
12 112 female    age.2014    31               age 2014
13 114 female    age.2014    NA               age 2014
14 315   male    age.2014    88               age 2014
15 539 female    age.2014    NA               age 2014
16 111   male income.2014  2500            income 2014
17 112 female income.2014   400            income 2014
18 114 female income.2014    NA            income 2014
19 315   male income.2014    NA            income 2014
20 539 female income.2014    NA            income 2014
21 111   male    age.2016    NA               age 2016
22 112 female    age.2016    33               age 2016
23 114 female    age.2016    NA               age 2016
24 315   male    age.2016    90               age 2016
25 539 female    age.2016    NA               age 2016
26 111   male income.2016    NA            income 2016
27 112 female income.2016   540            income 2016
28 114 female income.2016   200            income 2016
29 315   male income.2016    NA            income 2016
30 539 female income.2016   300            income 2016

Bước 2: Sắp xếp dataset gọn gàng trước khi reshape qua dạng wide. Ta thấy R đã chuẩn lại về cho ballance bộ dataset, tức là các giá trị id đều đầy đủ số liệu theo năm.

library(dplyr)

survey_long_2 <- survey_long_1[, -3]

survey_long_2 |> dplyr::arrange(id, year) -> survey_long_2

survey_long_2[, c(1, 2, 4, 3, 5)] -> survey_long_2

survey_long_2
    id gender chi_tieu_theo_doi value year
1  111   male               age    56 2012
2  111   male            income  1000 2012
3  111   male               age    58 2014
4  111   male            income  2500 2014
5  111   male               age    NA 2016
6  111   male            income    NA 2016
7  112 female               age    29 2012
8  112 female            income    NA 2012
9  112 female               age    31 2014
10 112 female            income   400 2014
11 112 female               age    33 2016
12 112 female            income   540 2016
13 114 female               age    NA 2012
14 114 female            income    NA 2012
15 114 female               age    NA 2014
16 114 female            income    NA 2014
17 114 female               age    NA 2016
18 114 female            income   200 2016
19 315   male               age    86 2012
20 315   male            income   440 2012
21 315   male               age    88 2014
22 315   male            income    NA 2014
23 315   male               age    90 2016
24 315   male            income    NA 2016
25 539 female               age    NA 2012
26 539 female            income    NA 2012
27 539 female               age    NA 2014
28 539 female            income    NA 2014
29 539 female               age    NA 2016
30 539 female            income   300 2016

Bước 3: Reshape trở về dạng wide để giống như dataset survey ban đầu.

survey_wide_ok <- reshape(survey_long_2, 
                         direction = "wide", # định vị kiểu spread dữ liệu dạng wide
                         idvar = c("id", "gender", "year"), # giữ lại 3 cột này
                         timevar = "chi_tieu_theo_doi", # spread ra theo cột `chi_tieu_theo_doi`
                         v.names = c("value") # chọn cột này để spread dữ liệu
                         ) 

row.names(survey_wide_ok) <- NULL

attributes(survey_wide_ok)$reshapeWide <- NULL

survey_wide_ok
    id gender year value.age value.income
1  111   male 2012        56         1000
2  111   male 2014        58         2500
3  111   male 2016        NA           NA
4  112 female 2012        29           NA
5  112 female 2014        31          400
6  112 female 2016        33          540
7  114 female 2012        NA           NA
8  114 female 2014        NA           NA
9  114 female 2016        NA          200
10 315   male 2012        86          440
11 315   male 2014        88           NA
12 315   male 2016        90           NA
13 539 female 2012        NA           NA
14 539 female 2014        NA           NA
15 539 female 2016        NA          300

Bước 4: Sửa lại tên cột.

survey_wide_ok_1 <- survey_wide_ok[, c(1, 2, 4, 5, 3)]

names(survey_wide_ok_1)[3] <- "age"
names(survey_wide_ok_1)[4] <- "income"

survey_wide_ok_1
    id gender age income year
1  111   male  56   1000 2012
2  111   male  58   2500 2014
3  111   male  NA     NA 2016
4  112 female  29     NA 2012
5  112 female  31    400 2014
6  112 female  33    540 2016
7  114 female  NA     NA 2012
8  114 female  NA     NA 2014
9  114 female  NA    200 2016
10 315   male  86    440 2012
11 315   male  88     NA 2014
12 315   male  90     NA 2016
13 539 female  NA     NA 2012
14 539 female  NA     NA 2014
15 539 female  NA    300 2016

Bước 5: Loại bỏ missing value và kiểm tra identical với survey ban đầu.

complete.cases(survey_wide_ok_1$age) -> check_1
complete.cases(survey_wide_ok_1$income) -> check_2

check_1 | check_2 -> check_3 # dùng OR để chọn cả 2 FALSE
# để loại missing value ở 2 cột `age` và `income`

survey_wide_ok_1[check_3, ] -> survey_wide_ok_2

row.names(survey_wide_ok_2) <- NULL

# lưu ý là cột `year` cần chuyển về numeric
survey_wide_ok_2$year <- as.numeric(survey_wide_ok_2$year)

# setdiff(survey, survey_wide_ok_2)

survey_wide_ok_2
    id gender age income year
1  111   male  56   1000 2012
2  111   male  58   2500 2014
3  112 female  29     NA 2012
4  112 female  31    400 2014
5  112 female  33    540 2016
6  114 female  NA    200 2016
7  315   male  86    440 2012
8  315   male  88     NA 2014
9  315   male  90     NA 2016
10 539 female  NA    300 2016
survey
    id gender age income year
1  111   male  56   1000 2012
2  111   male  58   2500 2014
3  112 female  29     NA 2012
4  112 female  31    400 2014
5  112 female  33    540 2016
6  114 female  NA    200 2016
7  315   male  86    440 2012
8  315   male  88     NA 2014
9  315   male  90     NA 2016
10 539 female  NA    300 2016
# nếu ở bước này nhìn dataset đã giống nhau rồi mà 
# identical vẫn false thì ta sắp xếp toàn bộ 
#các cột trong dataset về cùng 1 cấu trúc sau đó mới so sánh
identical(survey, survey_wide_ok_2)
[1] TRUE

Bước 6 (nếu kỹ hơn): Chuẩn lại toàn bộ thứ tự thành phần trong dataset

survey |> dplyr::arrange(!!! rlang::syms(names(survey))) -> survey_chuan

survey_chuan
    id gender age income year
1  111   male  56   1000 2012
2  111   male  58   2500 2014
3  112 female  29     NA 2012
4  112 female  31    400 2014
5  112 female  33    540 2016
6  114 female  NA    200 2016
7  315   male  86    440 2012
8  315   male  88     NA 2014
9  315   male  90     NA 2016
10 539 female  NA    300 2016
survey_wide_ok_2 |> dplyr::arrange(!!! rlang::syms(names(survey_wide_ok_2))) -> survey_wide_ok_2_chuan

identical(survey_chuan, survey_wide_ok_2_chuan)
[1] TRUE

25.2 Các tình huống reshape thường gặp

25.2.1 Chuyển dữ liệu từ hàng ngang sang hàng dọc

df_cor_small <- readRDS("df_cor_small.rds")

df_cor_small
         SV   DT Sex K12Toan K12Van K12NN K12Ly K12Hoa
1  22010001 Kinh Nam     8.2    7.5   9.1   8.4    8.6
2  22010002 Kinh  Nữ     9.2    8.4   9.5   9.5    9.1
3  22010003 Kinh  Nữ     8.9    8.8   8.0   8.3    9.2
4  22010004 Kinh  Nữ     9.2    9.1   9.0   8.0    8.1
5  22010005  Tày  Nữ     6.7    7.9   5.9   6.3    7.0
6  22010006 Kinh  Nữ     9.1    8.5   8.4   9.1    9.5
7  22010007 Kinh Nam     9.5    7.5   9.7  10.0    8.2
8  22010008 Kinh  Nữ     9.2    7.2   9.4   9.3    9.2
9  22010009 Kinh  Nữ     8.8    8.0   7.7   8.2    8.4
10 22010010 Kinh  Nữ     7.2    5.6   7.6   7.8    7.8
library(tidyr)
long_df <- tidyr:::pivot_longer(df_cor_small, 
                        cols = c(4:8), # vị trí các cột cần rã về dạng long
                        # Lệnh này tìm kiếm theo tên cột
                        # bản chất là dùng function grep()
                        # cols = starts_with("K12"),
                        names_to = "Test", 
                        values_to = "Score")

long_df <- as.data.frame(long_df)

long_df
         SV   DT Sex    Test Score
1  22010001 Kinh Nam K12Toan   8.2
2  22010001 Kinh Nam  K12Van   7.5
3  22010001 Kinh Nam   K12NN   9.1
4  22010001 Kinh Nam   K12Ly   8.4
5  22010001 Kinh Nam  K12Hoa   8.6
6  22010002 Kinh  Nữ K12Toan   9.2
7  22010002 Kinh  Nữ  K12Van   8.4
8  22010002 Kinh  Nữ   K12NN   9.5
9  22010002 Kinh  Nữ   K12Ly   9.5
10 22010002 Kinh  Nữ  K12Hoa   9.1
11 22010003 Kinh  Nữ K12Toan   8.9
12 22010003 Kinh  Nữ  K12Van   8.8
13 22010003 Kinh  Nữ   K12NN   8.0
14 22010003 Kinh  Nữ   K12Ly   8.3
15 22010003 Kinh  Nữ  K12Hoa   9.2
16 22010004 Kinh  Nữ K12Toan   9.2
17 22010004 Kinh  Nữ  K12Van   9.1
18 22010004 Kinh  Nữ   K12NN   9.0
19 22010004 Kinh  Nữ   K12Ly   8.0
20 22010004 Kinh  Nữ  K12Hoa   8.1
21 22010005  Tày  Nữ K12Toan   6.7
22 22010005  Tày  Nữ  K12Van   7.9
23 22010005  Tày  Nữ   K12NN   5.9
24 22010005  Tày  Nữ   K12Ly   6.3
25 22010005  Tày  Nữ  K12Hoa   7.0
26 22010006 Kinh  Nữ K12Toan   9.1
27 22010006 Kinh  Nữ  K12Van   8.5
28 22010006 Kinh  Nữ   K12NN   8.4
29 22010006 Kinh  Nữ   K12Ly   9.1
30 22010006 Kinh  Nữ  K12Hoa   9.5
31 22010007 Kinh Nam K12Toan   9.5
32 22010007 Kinh Nam  K12Van   7.5
33 22010007 Kinh Nam   K12NN   9.7
34 22010007 Kinh Nam   K12Ly  10.0
35 22010007 Kinh Nam  K12Hoa   8.2
36 22010008 Kinh  Nữ K12Toan   9.2
37 22010008 Kinh  Nữ  K12Van   7.2
38 22010008 Kinh  Nữ   K12NN   9.4
39 22010008 Kinh  Nữ   K12Ly   9.3
40 22010008 Kinh  Nữ  K12Hoa   9.2
41 22010009 Kinh  Nữ K12Toan   8.8
42 22010009 Kinh  Nữ  K12Van   8.0
43 22010009 Kinh  Nữ   K12NN   7.7
44 22010009 Kinh  Nữ   K12Ly   8.2
45 22010009 Kinh  Nữ  K12Hoa   8.4
46 22010010 Kinh  Nữ K12Toan   7.2
47 22010010 Kinh  Nữ  K12Van   5.6
48 22010010 Kinh  Nữ   K12NN   7.6
49 22010010 Kinh  Nữ   K12Ly   7.8
50 22010010 Kinh  Nữ  K12Hoa   7.8

25.2.2 Chuyển dữ liệu từ hàng dọc sang hàng ngang

Trường hợp 1: Dữ liệu đơn giản

ok_1 <- readRDS("ok_1.rds")

ok_1_short <- ok_1[ , c(2, 3, 4)]

ok_1_short
# A tibble: 10 × 3
   NAME       variable estimate
   <chr>      <chr>       <dbl>
 1 Alabama    income      24476
 2 Alabama    rent          747
 3 Alaska     income      32940
 4 Alaska     rent         1200
 5 Arizona    income      27517
 6 Arizona    rent          972
 7 Arkansas   income      23789
 8 Arkansas   rent          709
 9 California income      29454
10 California rent         1358

Ta chuyển qua dạng wide

ok_1_short |> tidyr:::pivot_wider(
  names_from = c(variable),
  values_from = c(estimate)
) -> ok_1_wide

ok_1_wide
# A tibble: 5 × 3
  NAME       income  rent
  <chr>       <dbl> <dbl>
1 Alabama     24476   747
2 Alaska      32940  1200
3 Arizona     27517   972
4 Arkansas    23789   709
5 California  29454  1358

Nếu muốn chuyển về dạng long thì áp dụng cách sau:

ok_1_long <- tidyr:::pivot_longer(ok_1_wide, 
                        cols = c("income", "rent"),
                        names_to = "variable", 
                        values_to = "estimate")

ok_1_long
# A tibble: 10 × 3
   NAME       variable estimate
   <chr>      <chr>       <dbl>
 1 Alabama    income      24476
 2 Alabama    rent          747
 3 Alaska     income      32940
 4 Alaska     rent         1200
 5 Arizona    income      27517
 6 Arizona    rent          972
 7 Arkansas   income      23789
 8 Arkansas   rent          709
 9 California income      29454
10 California rent         1358

Trường hợp 2: Dữ liệu phức tạp

Chuyển qua dạng wide

ok_1 <- readRDS("ok_1.rds")

ok_1
# A tibble: 10 × 5
   GEOID NAME       variable estimate   moe
   <chr> <chr>      <chr>       <dbl> <dbl>
 1 01    Alabama    income      24476   136
 2 01    Alabama    rent          747     3
 3 02    Alaska     income      32940   508
 4 02    Alaska     rent         1200    13
 5 04    Arizona    income      27517   148
 6 04    Arizona    rent          972     4
 7 05    Arkansas   income      23789   165
 8 05    Arkansas   rent          709     5
 9 06    California income      29454   109
10 06    California rent         1358     3
ok_1 |> pivot_wider(
  names_from = c(variable),
  values_from = c(estimate, moe)
) -> ok_1_wide_yes

Nếu muốn chuyển về dạng long

ok_1_long_yes <- tidyr:::pivot_longer(ok_1_wide_yes, 
                        cols = 3:6,
                        names_to = "variable",
                        values_to = "value")

ok_1_long_yes # đây là dạng true long
# A tibble: 20 × 4
   GEOID NAME       variable        value
   <chr> <chr>      <chr>           <dbl>
 1 01    Alabama    estimate_income 24476
 2 01    Alabama    estimate_rent     747
 3 01    Alabama    moe_income        136
 4 01    Alabama    moe_rent            3
 5 02    Alaska     estimate_income 32940
 6 02    Alaska     estimate_rent    1200
 7 02    Alaska     moe_income        508
 8 02    Alaska     moe_rent           13
 9 04    Arizona    estimate_income 27517
10 04    Arizona    estimate_rent     972
11 04    Arizona    moe_income        148
12 04    Arizona    moe_rent            4
13 05    Arkansas   estimate_income 23789
14 05    Arkansas   estimate_rent     709
15 05    Arkansas   moe_income        165
16 05    Arkansas   moe_rent            5
17 06    California estimate_income 29454
18 06    California estimate_rent    1358
19 06    California moe_income        109
20 06    California moe_rent            3

Nếu muốn chuyển về dạng semi-long

ok_1_long_yes_a <- tidyr:::pivot_longer(ok_1_wide_yes[ , 1:4], 
                        cols = 3:4,
                        names_to = "variable",
                        values_to = "estimate")

ok_1_long_yes_a$variable <- gsub(pattern = "estimate_",
                                 replacement = "",
                                 x = ok_1_long_yes_a$variable)

ok_1_long_yes_b <- tidyr:::pivot_longer(ok_1_wide_yes[ , c(1, 2, 5, 6)], 
                        cols = 3:4,
                        names_to = "variable",
                        values_to = "moe")

ok_1_long_yes_b$variable <- gsub(pattern = "moe_",
                                 replacement = "",
                                 x = ok_1_long_yes_b$variable)

ok_1_long_yes_b
# A tibble: 10 × 4
   GEOID NAME       variable   moe
   <chr> <chr>      <chr>    <dbl>
 1 01    Alabama    income     136
 2 01    Alabama    rent         3
 3 02    Alaska     income     508
 4 02    Alaska     rent        13
 5 04    Arizona    income     148
 6 04    Arizona    rent         4
 7 05    Arkansas   income     165
 8 05    Arkansas   rent         5
 9 06    California income     109
10 06    California rent         3
ok_1_long_yes_final <- merge(x = ok_1_long_yes_a,
                             y = ok_1_long_yes_b,
                             all = TRUE,
                             by = c("GEOID",
                                    "NAME",
                                    "variable"))

ok_1_long_yes_final
   GEOID       NAME variable estimate moe
1     01    Alabama   income    24476 136
2     01    Alabama     rent      747   3
3     02     Alaska   income    32940 508
4     02     Alaska     rent     1200  13
5     04    Arizona   income    27517 148
6     04    Arizona     rent      972   4
7     05   Arkansas   income    23789 165
8     05   Arkansas     rent      709   5
9     06 California   income    29454 109
10    06 California     rent     1358   3
identical(as.data.frame(ok_1), ok_1_long_yes_final)
[1] TRUE

25.2.3 Tham khảo

  • https://www.magesblog.com/post/2012-02-09-reshape-function/

  • https://search.r-project.org/CRAN/refmans/splitstackshape/html/Reshape.html

  • https://www.mjandrews.org/blog/pivots/