таблица частот для многих переменных, включая проценты и разделенных по группам - PullRequest
0 голосов
/ 27 мая 2020

Проблема Я пытаюсь создать таблицу частот для многих переменных, включая проценты и разделенные по группам.

Я пытаюсь использовать Rmarkdown для написания статьи в первый раз, и такая таблица довольно часто встречается в статьях, как в этой ссылке: Таблица частот из бумаги

Необработанные данные имеют аккуратную форму:

# A tibble: 500 x 5
    age    age_group    cond_a    cond_b    cond_c
    <int>  <chr>        <lgl>     <lgl>     <lgl>   
 1     0   0-2          FALSE     TRUE      FALSE   
 2     7   3-60         FALSE     FALSE     FALSE   
 3    42   3-60         TRUE      FALSE     FALSE   
 4     0   0-2          FALSE     FALSE     TRUE   
 5     0   0-2          TRUE      FALSE     TRUE   
 6     3   3-60         FALSE     FALSE     FALSE   
 7    64   60+          FALSE     TRUE      FALSE   
 8    70   60+          FALSE     TRUE      FALSE   
 9     4   3-60         TRUE      FALSE     FALSE   
10    24   3-60         FALSE     FALSE     TRUE 

Желаемый результат Результат должен быть примерно таким (первая строка с n необязательна):

         `0-2`       `3-60`      `60+`
n         20          330         150
cond_a    1 (5%)      33 (10%)    30 (20%)
cond_b    5 (25%)     66 (20%)    60 (40%)

Попытка подхода До сих пор я пытался использовать dplyr и tidyr, после того, как все пакеты, которые я смог найти (например, summarytools, questionr), не работали с группами или были ограничены отдельными переменными.

Я смог разделить только на весь набор данных (per = count / nrow (df)), но знаменатель должен быть количеством образцов по группе.

count / sum (count) тоже не будут работать (как описано в предыдущей публикации ), потому что одновременно могут возникать несколько условий.

df %>%  
  group_by(age_group) %>%
  summarise(
    cond_a = sum(cond_a, na.rm = TRUE),
    cond_b = sum(cond_b, na.rm = TRUE),
    cond_c = sum(cond_a, na.rm = TRUE)
    ) %>% 
  pivot_longer(-age_group, names_to = "variable", values_to="count") %>% 
  group_by(age_group) %>%
  mutate(per = count/nrow(df),
         output = paste0(count," (",sprintf("%.2f", per*100),"%)")) %>%
  select(-count,-per) %>%
  pivot_wider(names_from = age_group_extended, values_from = output)

Дополнительная информация В идеале я хотел бы добавить некоторые переменные, которые не нужно подсчитывать, например, средний возраст, медиана баллов и т. Д. c. Если есть способ добавить их, помимо объединения таблиц, было бы полезно.

Ответы [ 2 ]

0 голосов
/ 28 мая 2020

Вы в основном составляете таблицы и вычисляете пропорции на основе таблиц, поэтому, вероятно, лучше придерживаться table и prop.table. Вот пример функции, с которой можно начать:

  myFun <- function(input, id, measure, display = FALSE) {
    x <- melt(as.data.table(input), id.vars = id, measure.vars = measure)
    x <- x[(value), table(variable, get(id))]
    y <- prop.table(x, 1) * 100
    if (display) {
      x[] <- sprintf("%s (%02.f%%)", x, y)
      as.data.frame.matrix(x)
    } else {
      list(x, y)
    }
  }

Функцию можно использовать следующим образом:

myFun(mydf, "age_group", c("cond_a", "cond_b", "cond_c"))
## [[1]]
##         
## variable 0-2 3-60 60+
##   cond_a   1    2   0
##   cond_b   1    0   2
##   cond_c   2    1   0
## 
## [[2]]
##         
## variable      0-2     3-60      60+
##   cond_a 33.33333 66.66667  0.00000
##   cond_b 33.33333  0.00000 66.66667
##   cond_c 66.66667 33.33333  0.00000
## 

Или вы можете установить display = TRUE и получить следующее:

myFun(mydf, "age_group", c("cond_a", "cond_b", "cond_c"), display = TRUE)
##            0-2    3-60     60+
## cond_a 1 (33%) 2 (67%) 0 (00%)
## cond_b 1 (33%) 0 (00%) 2 (67%)
## cond_c 2 (67%) 1 (33%) 0 (00%)

Вот примеры данных, которые я использовал:

mydf <- structure(list(age = c(0L, 7L, 42L, 0L, 0L, 3L, 64L, 70L, 4L, 
    24L), age_group = c("0-2", "3-60", "3-60", "0-2", "0-2", "3-60", 
    "60+", "60+", "3-60", "3-60"), cond_a = c(FALSE, FALSE, TRUE, 
    FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE), cond_b = c(TRUE, 
    FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE), 
        cond_c = c(FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, 
        FALSE, FALSE, TRUE)), row.names = c("1", "2", "3", "4", "5", 
    "6", "7", "8", "9", "10"), class = "data.frame")

В идеале «display» не было бы аргументом функции, а скорее, вы бы создали другой метод print. Таким образом, значения по-прежнему доступны в list, хотя то, что печатается на экране, может быть отформатировано по-другому.


Если вы делаете это для печати, вы можете посмотреть в пакет tables. Вот подход, который я бы использовал в этом случае:

library(data.table)
library(tables)
DT <- melt(as.data.table(mydf), measure.vars = patterns("cond"))[(value)]

tabular((variable) ~ (Group = factor(age_group)) * ((n = 1) + 
    Percent("row")) * Format(digits = 1), data = DT)
##                                                 
##           Group                                 
##           0-2           3-60         60+        
##  variable n     Percent n    Percent n   Percent
##  cond_a    1    33       2   67       0   0     
##  cond_b    1    33       0    0       2  67     
##  cond_c    2    67       1   33       0   0     
0 голосов
/ 27 мая 2020

Вот подход с dplyr и tidyr.

Хитрость в том, что вызов df$cond_a позволяет нам sum из исходного df без групп.

Кроме того, изменение числовых c столбцов на character позволяет им находиться в одном столбце с другими результатами.

library(dplyr)
library(tidyr)
df %>%
  group_by(age_group) %>%
  summarize(n = as.character(n()),
            median_age = as.character(median(age)),
            cond_a = paste0(sum(cond_a, na.rm = TRUE)," (",
                            round(sum(cond_a, na.rm = TRUE)/sum(df$cond_a) * 100,0),
                            "%)"),
            cond_b = paste0(sum(cond_b, na.rm = TRUE)," (",
                            round(sum(cond_b, na.rm = TRUE)/sum(df$cond_b) * 100,0),
                            "%)"),
            cond_c = paste0(sum(cond_c, na.rm = TRUE)," (",
                            round(sum(cond_c, na.rm = TRUE)/sum(df$cond_c) * 100,0),
                            "%)")) %>%
  pivot_longer(-age_group) %>%
  pivot_wider(names_from = "age_group", values_from = "value")
## A tibble: 5 x 4
#  name       `0-2`   `3-60`  `60+`  
#  <chr>      <chr>   <chr>   <chr>  
#1 n          3       5       2      
#2 median_age 0       7       67     
#3 cond_a     1 (33%) 2 (67%) 0 (0%) 
#4 cond_b     1 (33%) 0 (0%)  2 (67%)
#5 cond_c     2 (67%) 1 (33%) 0 (0%) 
...