13  Nhóm dữ liệu

Chương này trình bày cách nhóm và tổng hợp dữ liệu phục vụ phân tích mô tả. Sử dụng các package trong hệ sinh thái tidyverse với các hàm phổ biến và dễ sử dụng.

Nhóm dữ liệu là một cấu phần cốt lõi trong quản lý và phân tích dữ liệu. Dữ liệu được nhóm tổng hợp thống kê theo nhóm và có thể được vẽ biểu đồ theo nhóm. Các hàm từ package dplyr (một phần của thư viện tidyverse) giúp việc nhóm dữ liệu và các thao tác tiếp theo trở nên dễ dàng hơn.

Chương này sẽ giải quyết các chủ đề sau:

13.1 Chuẩn bị

Gọi package

Đoạn code này hiển thị cách gọi các package cần thiết cho phân tích. Trong cuốn này, chúng ta nhấn mạnh đến hàm p_load() từ package pacman, sẽ cài đặt packahe nếu cần gọi để sử dụng. Bạn cũng có thể gọi các package đã cài đặt với hàm library() từ base R. Xem chương R cơ bản để biết thêm thông tin về các package trong R.

pacman::p_load(
  rio,       # to import data
  here,      # to locate files
  tidyverse, # to clean, handle, and plot the data (includes dplyr)
  janitor)   # adding total rows and columns

Nhập dữ liệu

Chúng ta nhập bộ dữ liệu về các trường hợp mô phỏng từ một vụ dịch Ebola. Nếu bạn muốn cùng thực hành, bấm vào đây để tải bộ liệu linelist “sạch” (tệp .rds). Bộ dữ liệu được nhập bằng hàm import() từ package rio. Xem chương Nhập xuất dữ liệu để biết các cách nhập dữ liệu khác nhau.

linelist <- import("linelist_cleaned.rds")

50 hàng đầu tiên của bộ số liệu linelist:

13.2 Nhóm

Hàm group_by() từ dplyr nhóm các hàng theo các giá trị duy nhất trong cột chỉ định. Nếu nhiều cột được chỉ định, các hàng được nhóm theo sự tổ hợp các giá trị thành giá trị duy nhất từ các cột. Mỗi giá trị duy nhất (hoặc tổ hợp các giá trị) tạo thành một nhóm. Những thay đổi sau đó đối với bộ dữ liệu hoặc các phép tính có thể thực hiện tùy theo đặc điểm của mỗi nhóm.

Ví dụ: lệnh bên dưới truy vấn bộ số liệu linelist và nhóm các hàng theo giá trị duy nhất trong cột outcome, lưu kết quả đầu ra dưới dạng bộ dữ liệu mới có tên ll_by_outcome. (Các) cột được nhóm được đặt bên trong dấu ngoặc đơn của hàm group_by().

ll_by_outcome <- linelist %>% 
  group_by(outcome)

Lưu ý rằng không có thay đổi dễ nhận thấy nào đối với bộ dữ liệu sau khi chạy lệnh group_by(), cho đến khi phối hợp thêm một số hàm từ dplyr khác như mutate(), summarise(), hoặc arrange() trong bộ dữ liệu “đã được nhóm”.

Tuy nhiên, bạn có thể “xem” các nhóm bằng cách in bộ dữ liệu. Khi bạn in một bộ dữ liệu được nhóm, bạn sẽ thấy nó đã được chuyển đổi thành một đối tượng dạng tibble, mà khi được in ra, sẽ hiển thị những nhóm nào đã được áp dụng và có bao nhiêu nhóm - được viết ngay phía trên hàng tiêu đề.

# print to see which groups are active
ll_by_outcome
# A tibble: 5,888 × 30
# Groups:   outcome [3]
   case_id generation date_infection date_onset date_hospitalisation
   <chr>        <dbl> <date>         <date>     <date>              
 1 5fe599           4 2014-05-08     2014-05-13 2014-05-15          
 2 8689b7           4 NA             2014-05-13 2014-05-14          
 3 11f8ea           2 NA             2014-05-16 2014-05-18          
 4 b8812a           3 2014-05-04     2014-05-18 2014-05-20          
 5 893f25           3 2014-05-18     2014-05-21 2014-05-22          
 6 be99c8           3 2014-05-03     2014-05-22 2014-05-23          
 7 07e3e8           4 2014-05-22     2014-05-27 2014-05-29          
 8 369449           4 2014-05-28     2014-06-02 2014-06-03          
 9 f393b4           4 NA             2014-06-05 2014-06-06          
10 1389ca           4 NA             2014-06-05 2014-06-07          
# ℹ 5,878 more rows
# ℹ 25 more variables: date_outcome <date>, outcome <chr>, gender <chr>,
#   age <dbl>, age_unit <chr>, age_years <dbl>, age_cat <fct>, age_cat5 <fct>,
#   hospital <chr>, lon <dbl>, lat <dbl>, infector <chr>, source <chr>,
#   wt_kg <dbl>, ht_cm <dbl>, ct_blood <dbl>, fever <chr>, chills <chr>,
#   cough <chr>, aches <chr>, vomit <chr>, temp <dbl>, time_admission <chr>,
#   bmi <dbl>, days_onset_hosp <dbl>

Nhóm duy nhất

Các nhóm được tạo phản ánh từng tổ hợp giá trị duy nhất dọc theo các cột được sử dụng để nhóm.

Để xem các nhóm và số hàng trong mỗi nhóm, hãy chuyển tiếp dữ liệu được nhóm đến hàm tally(). Nếu chỉ cần xem các nhóm duy nhất mà không cần số lượng, bạn có thể sử dụng hàm group_keys().

Dưới đây là ba giá trị duy nhất trong cột được nhóm là outcome: “Death”, “Recover”, và NA. Ta thấy rằng đã có nrow(linelist %>% filter(outcome == "Death")) tử vong, nrow(linelist %>% filter(outcome == "Recover")) hồi phục, và nrow(linelist %>% filter(is.na(outcome))) không có outcome nào được ghi nhận.

linelist %>% 
  group_by(outcome) %>% 
  tally()
# A tibble: 3 × 2
  outcome     n
  <chr>   <int>
1 Death    2582
2 Recover  1983
3 <NA>     1323

Bạn có thể nhóm nhiều hơn một cột. Dưới đây, bộ dữ liệu được nhóm theo outcomegender, sau đó được đánh số. Lưu ý cách mỗi kết hợp duy nhất của outcomegender được tổ hợp thành nhóm riêng - bao gồm cả các giá trị bị thiếu ở một trong hai cột.

linelist %>% 
  group_by(outcome, gender) %>% 
  tally()
# A tibble: 9 × 3
# Groups:   outcome [3]
  outcome gender     n
  <chr>   <chr>  <int>
1 Death   f       1227
2 Death   m       1228
3 Death   <NA>     127
4 Recover f        953
5 Recover m        950
6 Recover <NA>      80
7 <NA>    f        627
8 <NA>    m        625
9 <NA>    <NA>      71

Cột mới

Bạn cũng có thể tạo một cột mới dùng để nhóm trong câu lệnh group_by(). Điều này tương đương với việc sử dụng hàm mutate() trước khi sử dụng hàm group_by(). Để tạo cột nhanh, cách này có thể hữu ích, nhưng để rõ ràng hơn trong code của bạn, hãy cân nhắc cách tạo cột này ở bước mutate() và sau đó chuyển tiếp đến hàm group_by().

# group dat based on a binary column created *within* the group_by() command
linelist %>% 
  group_by(
    age_class = ifelse(age >= 18, "adult", "child")) %>% 
  tally(sort = T)
# A tibble: 3 × 2
  age_class     n
  <chr>     <int>
1 child      3618
2 adult      2184
3 <NA>         86

Thêm/bỏ cột được nhóm

Mặc định, nếu bạn chạy lệnh group_by() trên dữ liệu đã được nhóm, các nhóm cũ sẽ bị xóa và (các) nhóm mới sẽ được áp dụng. Nếu bạn muốn thêm nhóm mới vào nhóm hiện có, hãy thêm đối số .add = TRUE.

# Grouped by outcome
by_outcome <- linelist %>% 
  group_by(outcome)

# Add grouping by gender in addition
by_outcome_gender <- by_outcome %>% 
  group_by(gender, .add = TRUE)

13.3 Giữ tất cả các nhóm

Nếu bạn nhóm trên một cột dạng factor, có thể có các thứ bậc của factor mà không có trong dữ liệu. Nếu bạn nhóm trên cột này, mặc định, những thứ bậc không có trong dữ liệu đó sẽ bị loại bỏ và không thuộc dạng nhóm. Để thay đổi điều này để tất cả các thứ bậc xuất hiện dưới dạng nhóm (ngay cả khi không có trong dữ liệu), đặt .drop = FALSE trong lệnh group_by().

13.4 Loại bỏ nhóm

Dữ liệu đã được nhóm sẽ vẫn được nhóm cho đến khi hủy nhóm bởi lệnh ungroup(). Nếu bạn quên hủy nhóm, nó có thể gây ra tính toán không chính xác! Dưới đây là ví dụ về việc loại bỏ tất cả các nhóm:

linelist %>% 
  group_by(outcome, gender) %>% 
  tally() %>% 
  ungroup()

Bạn cũng có thể loại bỏ nhóm với các cột cụ thể, bằng cách đặt tên cột bên trong lệnh ungroup().

linelist %>% 
  group_by(outcome, gender) %>% 
  tally() %>% 
  ungroup(gender) # remove the grouping by gender, leave grouping by outcome

GHI CHÚ: Hàm count() tự động hủy nhóm dữ liệu sau khi đếm.

13.5 Tổng hợp

Xem package dplyr trong chương Bảng mô tả để biết mô tả chi tiết về cách tạo bảng tổng hợp với hàm summarise(). Ở đây chúng ta giải quyết ngắn gọn cách hoạt động thay đổi khi áp dụng cho dữ liệu được nhóm.

Hàm dplyr summarise() (hoặc summarize()) lấy một data frame và chuyển nó thành một data frame tổng hợp mới, với các cột chứa thông tin các giá trị thống kê do bạn xác định. Trên bộ dữ liệu chưa nhóm, các tổng hợp thống kê sẽ được tính toán từ tất cả các hàng. Việc áp dụng hàm summarise() vào dữ liệu được nhóm sẽ tổng hợp những giá trị thống kê cho từng nhóm.

Cú pháp của hàm summarise() là bạn cung cấp tên của (các) cột tổng hợp mới, một dấu bằng và sau đó là một hàm thống kê để áp dụng cho dữ liệu, như được trình bày bên dưới. Ví dụ: min(), max(), median(), hoặc sd(). Trong hàm thống kê, hãy liệt kê cột sẽ được áp dụng và bất kỳ đối số nào có liên quan (ví dụ: na.rm = TRUE). Bạn có thể sử dụng hàm sum() để đếm số hàng đáp ứng tiêu chí logic (với dấu bằng kép ==).

Dưới đây là một ví dụ về hàm summarise() được áp dụng mà không có dữ liệu được nhóm. Các giá trị thống kê trả về được tạo ra từ toàn bộ bộ dữ liệu.

# summary statistics on ungrouped linelist
linelist %>% 
  summarise(
    n_cases  = n(),
    mean_age = mean(age_years, na.rm=T),
    max_age  = max(age_years, na.rm=T),
    min_age  = min(age_years, na.rm=T),
    n_males  = sum(gender == "m", na.rm=T))
  n_cases mean_age max_age min_age n_males
1    5888 16.01831      84       0    2803

Ngược lại, bên dưới là câu lệnh summarise() tương tự được áp dụng cho dữ liệu được nhóm. Các số liệu thống kê được tính toán cho từng nhóm kết quả. Lưu ý cách các cột dùng để nhóm sẽ chuyển sang data frame mới.

# summary statistics on grouped linelist
linelist %>% 
  group_by(outcome) %>% 
  summarise(
    n_cases  = n(),
    mean_age = mean(age_years, na.rm=T),
    max_age  = max(age_years, na.rm=T),
    min_age  = min(age_years, na.rm=T),
    n_males    = sum(gender == "m", na.rm=T))
# A tibble: 3 × 6
  outcome n_cases mean_age max_age min_age n_males
  <chr>     <int>    <dbl>   <dbl>   <dbl>   <int>
1 Death      2582     15.9      76       0    1228
2 Recover    1983     16.1      84       0     950
3 <NA>       1323     16.2      69       0     625

MẸO: Hàm tổng hợp hoạt động với cả cách viết của kiểu Anh và Mỹ - summarise()summarize() đều chỉ định cùng cùng một hàm.

13.6 Counts và tallies

count()tally() đều cung cấp tính năng tương tự nhưng thực chất là khác nhau. Đọc thêm về sự khác biệt giữa tally()count() tại đây.

tally()

tally() là viết tắt của summarise(n = n())không nhóm dữ liệu. Do đó, để có thể kiểm đếm theo nhóm, nó phải theo sau lệnh group_by(). Bạn có thể thêm sort = TRUE để xem các nhóm lớn nhất trước tiên.

linelist %>% 
  tally()
     n
1 5888
linelist %>% 
  group_by(outcome) %>% 
  tally(sort = TRUE)
# A tibble: 3 × 2
  outcome     n
  <chr>   <int>
1 Death    2582
2 Recover  1983
3 <NA>     1323

count()

Ngược lại, hàm count() hoạt động như sau:

  1. Áp dụng group_by() trên (các) cột đã chỉ định
  2. Áp dụng summarise() và trả về cột n với số lượng dòng mỗi nhóm
  3. Áp dụng ungroup()
linelist %>% 
  count(outcome)
  outcome    n
1   Death 2582
2 Recover 1983
3    <NA> 1323

Cũng giống như với hàm group_by(), bạn có thể tạo một cột mới trong lệnh count():

linelist %>% 
  count(age_class = ifelse(age >= 18, "adult", "child"), sort = T)
  age_class    n
1     child 3618
2     adult 2184
3      <NA>   86

count() có thể được sử dụng nhiều lần, với tính năng “cuộn lên”. Ví dụ: để tổng hợp số lượng bệnh viện hiện có cho mỗi giới tính, hãy chạy lệnh như sau. Lưu ý, tên của cột cuối cùng đã được thay đổi từ tên mặc định là “n” để cho rõ ràng (với name =).

linelist %>% 
  # produce counts by unique outcome-gender groups
  count(gender, hospital) %>% 
  # gather rows by gender (3) and count number of hospitals per gender (6)
  count(gender, name = "hospitals per gender" ) 
  gender hospitals per gender
1      f                    6
2      m                    6
3   <NA>                    6

Thêm giá trị đếm

Ngược lại với count()summarise(), bạn có thể sử dụng add_count() để thêm một cột mới n với số lượng hàng cho mỗi nhóm trong khi vẫn giữ lại tất cả các cột khác trong dữ liệu.

Điều này có nghĩa là số lượng của nhóm, trong cột mới n, sẽ được in ra trong mỗi hàng của nhóm. Với mục đích minh họa, chúng tôi thêm cột này và sau đó sắp xếp lại các cột để dễ xem hơn. Xem mục bên dưới về bộ lọc theo kích thước nhóm để biết một ví dụ khác.

linelist %>% 
  as_tibble() %>%                   # convert to tibble for nicer printing 
  add_count(hospital) %>%           # add column n with counts by hospital
  select(hospital, n, everything()) # re-arrange for demo purposes
# A tibble: 5,888 × 31
   hospital                       n case_id generation date_infection date_onset
   <chr>                      <int> <chr>        <dbl> <date>         <date>    
 1 Other                        885 5fe599           4 2014-05-08     2014-05-13
 2 Missing                     1469 8689b7           4 NA             2014-05-13
 3 St. Mark's Maternity Hosp…   422 11f8ea           2 NA             2014-05-16
 4 Port Hospital               1762 b8812a           3 2014-05-04     2014-05-18
 5 Military Hospital            896 893f25           3 2014-05-18     2014-05-21
 6 Port Hospital               1762 be99c8           3 2014-05-03     2014-05-22
 7 Missing                     1469 07e3e8           4 2014-05-22     2014-05-27
 8 Missing                     1469 369449           4 2014-05-28     2014-06-02
 9 Missing                     1469 f393b4           4 NA             2014-06-05
10 Missing                     1469 1389ca           4 NA             2014-06-05
# ℹ 5,878 more rows
# ℹ 25 more variables: date_hospitalisation <date>, date_outcome <date>,
#   outcome <chr>, gender <chr>, age <dbl>, age_unit <chr>, age_years <dbl>,
#   age_cat <fct>, age_cat5 <fct>, lon <dbl>, lat <dbl>, infector <chr>,
#   source <chr>, wt_kg <dbl>, ht_cm <dbl>, ct_blood <dbl>, fever <chr>,
#   chills <chr>, cough <chr>, aches <chr>, vomit <chr>, temp <dbl>,
#   time_admission <chr>, bmi <dbl>, days_onset_hosp <dbl>

Thêm giá trị tổng

Để dễ dàng thêm tổng các hàng hoặc cột sau khi sử dụng tally() hoặc count(), hãy xem mục janitor trong chương Bảng mô tả. Package này cung cấp các hàm như adorn_totals()adorn_percentages() để thêm tổng và chuyển đổi sang hiển thị tỷ lệ phần trăm. Dưới đây là một ví dụ ngắn gọn:

linelist %>%                                  # case linelist
  tabyl(age_cat, gender) %>%                  # cross-tabulate counts of two columns
  adorn_totals(where = "row") %>%             # add a total row
  adorn_percentages(denominator = "col") %>%  # convert to proportions with column denominator
  adorn_pct_formatting() %>%                  # convert proportions to percents
  adorn_ns(position = "front") %>%            # display as: "count (percent)"
  adorn_title(                                # adjust titles
    row_name = "Age Category",
    col_name = "Gender")
                      Gender                            
 Age Category              f              m          NA_
          0-4   640  (22.8%)   416  (14.8%)  39  (14.0%)
          5-9   641  (22.8%)   412  (14.7%)  42  (15.1%)
        10-14   518  (18.5%)   383  (13.7%)  40  (14.4%)
        15-19   359  (12.8%)   364  (13.0%)  20   (7.2%)
        20-29   468  (16.7%)   575  (20.5%)  30  (10.8%)
        30-49   179   (6.4%)   557  (19.9%)  18   (6.5%)
        50-69     2   (0.1%)    91   (3.2%)   2   (0.7%)
          70+     0   (0.0%)     5   (0.2%)   1   (0.4%)
         <NA>     0   (0.0%)     0   (0.0%)  86  (30.9%)
        Total 2,807 (100.0%) 2,803 (100.0%) 278 (100.0%)

Để thêm các hàng tổng phức tạp hơn bao gồm các thống kê tổng hợp khác, hãy xem mục sau trong chương Bảng mô tả.

13.7 Nhóm theo ngày

Khi nhóm dữ liệu theo ngày, bạn phải có (hoặc tạo) một cột cho đơn vị ngày quan tâm - ví dụ: “ngày”, “tuần dịch tễ”, “tháng”, v.v. Bạn có thể tạo cột này bằng cách sử dụng floor_date() từ lubridate, như được giải thích trong phần Tuần dịch tễ học trong chương Làm việc với ngày tháng. Khi bạn có cột này, bạn có thể sử dụng count() từ dplyr để nhóm các hàng theo các giá trị ngày duy nhất đó và thu về giá trị số lượng tổng hợp.

Một bước bổ sung phổ biến cho các tình huống với ngày/tháng, là “điền vào” bất kỳ ngày nào trong chuỗi ngày không có trong dữ liệu. Sử dụng complete() từ tidyr để chuỗi ngày tổng hợp được hoàn chỉnh bao gồm tất cả các đơn vị ngày có thể có trong phạm vi. Nếu không có bước này, một tuần không có trường hợp nào được báo cáo có thể không xuất hiện trong dữ liệu của bạn!

Trong complete(), bạn xác định lại cột ngày của mình dưới dạng một chuỗi ngày seq.Date() từ giá trị nhỏ nhất đến giá trị lớn nhất - do đó mà ngày/tháng được mở rộng. Mặc định, giá trị số lượng trong bất kỳ hàng “mở rộng” mới nào sẽ là NA. Bạn có thể đặt chúng thành 0 bằng cách sử dụng argument fill = của hàm complete(), trong đó yêu cầu một danh sách được đặt tên (nếu cột số lượng của bạn được đặt tên là n, hãy thêm fill = list(n = 0)). Xem ?complete để biết chi tiết và xem ví dụ trong chương Làm việc với ngày tháng.

Các trường hợp trong linelist theo ngày

Dưới đây là một ví dụ về nhóm các trường hợp theo ngày mà không sử dụng complete(). Lưu ý các hàng đầu tiên bỏ qua các ngày mà không có trường hợp nào.

daily_counts <- linelist %>% 
  drop_na(date_onset) %>%        # remove that were missing date_onset
  count(date_onset)              # count number of rows per unique date

Dưới đây, chúng ta thêm lệnh complete() để đảm bảo mỗi ngày trong phạm vi đều được hiển thị.

daily_counts <- linelist %>% 
  drop_na(date_onset) %>%                 # remove case missing date_onset
  count(date_onset) %>%                   # count number of rows per unique date
  complete(                               # ensure all days appear even if no cases
    date_onset = seq.Date(                # re-define date colume as daily sequence of dates
      from = min(date_onset, na.rm=T), 
      to = max(date_onset, na.rm=T),
      by = "day"),
    fill = list(n = 0))                   # set new filled-in rows to display 0 in column n (not NA as default) 

Các trường hợp trong linelist theo tuần

Nguyên tắc tương tự có thể được áp dụng cho tuần. Đầu tiên, hãy tạo một cột mới các trường hợp theo tuần bằng cách sử dụng floor_date() với unit = "week". Sau đó, sử dụng count() như trên để đạt được số lượng ca hàng tuần. Kết thúc bằng hàm complete() để đảm bảo rằng tất cả các tuần đều được hiển thị, ngay cả khi chúng không chứa ca nào.

# Make dataset of weekly case counts
weekly_counts <- linelist %>% 
  drop_na(date_onset) %>%                 # remove cases missing date_onset
  mutate(week = lubridate::floor_date(date_onset, unit = "week")) %>%  # new column of week of onset
  count(week) %>%                         # group data by week and count rows per group
  complete(                               # ensure all days appear even if no cases
    week = seq.Date(                      # re-define date colume as daily sequence of dates
      from = min(week, na.rm=T), 
      to = max(week, na.rm=T),
      by = "week"),
    fill = list(n = 0))                   # set new filled-in rows to display 0 in column n (not NA as default) 

Đây là 50 hàng đầu tiên của bộ dữ liệu khi trả kết quả:

Các trường hợp trong linelist theo tháng

Để tổng hợp các trường hợp theo tháng, hãy sử dụng lại floor_date() từ package lubridate, nhưng với argument unit = "months". Điều này làm tròn xuống các ngày thành ngày đầu tiên của tháng. Đầu ra sẽ là định dạng ngày. Lưu ý rằng trong bước complete(), chúng ta cũng sử dụng by = "months".

# Make dataset of monthly case counts
monthly_counts <- linelist %>% 
  drop_na(date_onset) %>% 
  mutate(month = lubridate::floor_date(date_onset, unit = "months")) %>%  # new column, 1st of month of onset
  count(month) %>%                          # count cases by month
  complete(
    month = seq.Date(
      min(month, na.rm=T),     # include all months with no cases reported
      max(month, na.rm=T),
      by="month"),
    fill = list(n = 0))

Số lượng đếm hàng ngày theo tuần

Để tổng hợp số lượng hàng ngày thành số lượng hàng tuần, hãy sử dụng floor_date() như trên. Tuy nhiên, hãy sử dụng group_by()summarize() thay vì count() bởi vì chúng ta cần tính tổng sum() số lượng trường hợp hàng ngày thay vì chỉ đếm số hàng mỗi tuần.

Số lượng đếm hàng ngày theo tuần

Để tổng hợp số lượng hàng ngày thành số lượng hàng tháng, hãy sử dụng floor_date() với unit = "month" như trên. Tuy nhiên, hãy sử dụng group_by()summarize() thay vì count() bởi vì chúng ta cần tính tổng sum() số lượng trường hợp hàng ngày thay vì chỉ đếm số hàng mỗi tháng.

13.8 Sắp xếp dữ liệu đã nhóm

Sử dụng hàm dplyr arrange() để sắp xếp các hàng trong bộ dữ liệu tương tự khi dữ liệu được nhóm lại, trừ khi bạn thiết lập đối số .by_group =TRUE. Trong trường hợp này, các hàng được sắp xếp thứ tự đầu tiên theo các cột nhóm và sau đó theo bất kỳ cột nào khác mà bạn chỉ định để arrange().

13.9 Lọc trên nhóm dữ liệu đã nhóm

filter()

Khi được áp dụng cùng với các hàm đánh giá bộ dữ liệu (như max(), min(), mean()), các hàm này sẽ được áp dụng cho các nhóm. Ví dụ: nếu bạn muốn lọc và giữ các hàng có bệnh nhân trên độ tuổi trung vị, hàm này sẽ áp dụng cho mỗi nhóm - lọc để giữ các hàng trên độ tuổi trung bình của nhóm.

Cắt hàng theo nhóm

Hàm dplyr slice(), mà lọc hàng dựa trên vị trí trong dữ liệu, cũng có thể được áp dụng cho mỗi nhóm. Hãy nhớ sắp xếp dữ liệu trong mỗi nhóm để có được “lát cắt” mong muốn.

Ví dụ: chỉ lấy 5 lần nhập viện gần nhất từ mỗi bệnh viện:

  1. Nhóm bộ số liệu linelist theo cột hospital
  2. Sắp xếp bản ghi từ gần nhất đến xa nhất theo date_hospitalisation trong mỗi nhóm bệnh viện
  3. Cắt để lấy 5 hàng đầu tiên từ mỗi bệnh viện
linelist %>%
  group_by(hospital) %>%
  arrange(hospital, date_hospitalisation) %>%
  slice_head(n = 5) %>% 
  arrange(hospital) %>%                            # for display
  select(case_id, hospital, date_hospitalisation)  # for display
# A tibble: 30 × 3
# Groups:   hospital [6]
   case_id hospital          date_hospitalisation
   <chr>   <chr>             <date>              
 1 20b688  Central Hospital  2014-05-06          
 2 d58402  Central Hospital  2014-05-10          
 3 b8f2fd  Central Hospital  2014-05-13          
 4 acf422  Central Hospital  2014-05-28          
 5 275cc7  Central Hospital  2014-05-28          
 6 d1fafd  Military Hospital 2014-04-17          
 7 974bc1  Military Hospital 2014-05-13          
 8 6a9004  Military Hospital 2014-05-13          
 9 09e386  Military Hospital 2014-05-14          
10 865581  Military Hospital 2014-05-15          
# ℹ 20 more rows

slice_head() - chọn n hàng từ trên xuống dưới
slice_tail() - chọn n hàng từ dưới lên trên
slice_sample() - chọn ngẫu nhiên n dòng
slice_min() - chọn n dòng với giá trị cao nhất trong cột order_by =, sử dụng with_ties = TRUE để giữ mối liên hệ
slice_max() - chọn n dòng với giá trị thấp nhất trong cột order_by =,sử dụng with_ties = TRUE để giữ mối liên hệ

Xem chương Loại bỏ trùng lặp để tham khảo thêm ví dụ và chi tiết về hàm slice().

Lọc theo quy mô nhóm

Hàm add_count() thêm một cột n vào dữ liệu gốc cho biết số hàng trong nhóm của hàng đó.

Như trình bày bên dưới, add_count() được áp dụng cho cột hospital, vì vậy các giá trị trong cột mới n phản ánh số hàng trong nhóm bệnh viện của hàng đó. Lưu ý cách các giá trị trong cột n được lặp lại. Trong ví dụ dưới đây, tên cột n có thể được thay đổi bằng cách sử dụng đối số name = trong add_count(). Với mục đích diễn giải, chúng ta sắp xếp lại các cột với select().

linelist %>% 
  as_tibble() %>% 
  add_count(hospital) %>%          # add "number of rows admitted to same hospital as this row" 
  select(hospital, n, everything())
# A tibble: 5,888 × 31
   hospital                       n case_id generation date_infection date_onset
   <chr>                      <int> <chr>        <dbl> <date>         <date>    
 1 Other                        885 5fe599           4 2014-05-08     2014-05-13
 2 Missing                     1469 8689b7           4 NA             2014-05-13
 3 St. Mark's Maternity Hosp…   422 11f8ea           2 NA             2014-05-16
 4 Port Hospital               1762 b8812a           3 2014-05-04     2014-05-18
 5 Military Hospital            896 893f25           3 2014-05-18     2014-05-21
 6 Port Hospital               1762 be99c8           3 2014-05-03     2014-05-22
 7 Missing                     1469 07e3e8           4 2014-05-22     2014-05-27
 8 Missing                     1469 369449           4 2014-05-28     2014-06-02
 9 Missing                     1469 f393b4           4 NA             2014-06-05
10 Missing                     1469 1389ca           4 NA             2014-06-05
# ℹ 5,878 more rows
# ℹ 25 more variables: date_hospitalisation <date>, date_outcome <date>,
#   outcome <chr>, gender <chr>, age <dbl>, age_unit <chr>, age_years <dbl>,
#   age_cat <fct>, age_cat5 <fct>, lon <dbl>, lat <dbl>, infector <chr>,
#   source <chr>, wt_kg <dbl>, ht_cm <dbl>, ct_blood <dbl>, fever <chr>,
#   chills <chr>, cough <chr>, aches <chr>, vomit <chr>, temp <dbl>,
#   time_admission <chr>, bmi <dbl>, days_onset_hosp <dbl>

Sau đó, nó trở nên dễ dàng để lọc các hàng trường hợp nhập viện tại một bệnh viện “nhỏ”, chẳng hạn như một bệnh viện tiếp nhận ít hơn 500 bệnh nhân:

linelist %>% 
  add_count(hospital) %>% 
  filter(n < 500)

13.10 Tạo cột mới trên dữ liệu được nhóm

Để giữ lại tất cả các cột và hàng (không tổng hợp) và thêm một cột mới chứa thống kê theo nhóm, hãy sử dụng hàm mutate() sau group_by() thay vì summarise().

Điều này hữu ích nếu bạn muốn nhóm thống kê trong bộ dữ liệu gốc với tất cả các cột khác được hiển thị - ví dụ: cho các phép tính so sánh một hàng với nhóm của nó.

Ví dụ: code dưới đây tính toán sự khác biệt giữa thời gian trễ nhập viện của một hàng và thời gian chậm trễ trung vị đối với bệnh viện đó. Các bước lần lượt là:

  1. Nhóm dữ liệu theo bệnh viện
  2. Sử dụng cột days_onset_hosp (nhập viện trễ) để tạo một cột mới chứa giá trị trễ trung bình tại bệnh viện của hàng đó
  3. Tính toán sự khác biệt giữa hai cột

Chúng ta chỉ lựa chọn select() những cột cần hiển thị, cho mục đích diễn giải.

linelist %>% 
  # group data by hospital (no change to linelist yet)
  group_by(hospital) %>% 
  
  # new columns
  mutate(
    # mean days to admission per hospital (rounded to 1 decimal)
    group_delay_admit = round(mean(days_onset_hosp, na.rm=T), 1),
    
    # difference between row's delay and mean delay at their hospital (rounded to 1 decimal)
    diff_to_group     = round(days_onset_hosp - group_delay_admit, 1)) %>%
  
  # select certain rows only - for demonstration/viewing purposes
  select(case_id, hospital, days_onset_hosp, group_delay_admit, diff_to_group)
# A tibble: 5,888 × 5
# Groups:   hospital [6]
   case_id hospital              days_onset_hosp group_delay_admit diff_to_group
   <chr>   <chr>                           <dbl>             <dbl>         <dbl>
 1 5fe599  Other                               2               2             0  
 2 8689b7  Missing                             1               2.1          -1.1
 3 11f8ea  St. Mark's Maternity…               2               2.1          -0.1
 4 b8812a  Port Hospital                       2               2.1          -0.1
 5 893f25  Military Hospital                   1               2.1          -1.1
 6 be99c8  Port Hospital                       1               2.1          -1.1
 7 07e3e8  Missing                             2               2.1          -0.1
 8 369449  Missing                             1               2.1          -1.1
 9 f393b4  Missing                             1               2.1          -1.1
10 1389ca  Missing                             2               2.1          -0.1
# ℹ 5,878 more rows

13.11 Chọn trên dữ liệu được nhóm

Hàm select() hoạt động trên dữ liệu được nhóm, nhưng các cột được nhóm luôn được bao gồm (ngay cả khi không được đề cập trong select()). Nếu bạn không muốn nhóm các cột này, trước tiên hãy bỏ nhóm bằng hàm ungroup().

13.12 Tài nguyên học liệu

Dưới đây là một số tài nguyên hữu ích cung cấp thêm thông tin:

Bạn có thể thực hiện bất kỳ hàm tổng hợp nào trên dữ liệu được nhóm; xem RStudio cheat sheet về biến đổi dữ liệu

Trang Data Carpentry của dplyr
Chương tham khảo hệ sinh thái tidyverse về group_by()grouping

Trang này về Thao tác dữ liệu

Tổng hợp có điều kiện với dplyr