Таблица частот пропорций с несколькими двоичными данными - PullRequest
2 голосов
/ 07 апреля 2020

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

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

country<-c('germany','germany','germany','USA','USA','USA','USA','germany','germany','USA')
sex<-c('female','male','male','female','female','female','male','female','female','female')
binary1<-c(1,1,0,1,0,1,0,0,0,1)
binary2<-c(0,1,0,1,1,1,0,1,0,1)
binary3<-c(0,1,1,1,0,1,0,0,0,1)
df<-cbind(country,sex,binary1,binary2,binary3)

Я хотел бы получить что-то вроде этого:

Germany
  Female                                Male
    variable       0          1           variable       0          1 
    binary1      66.7%      33.3%         binary1      50.0%       50.0%    
    binary2      66.7%      33.3%         binary2      50.0%       50.0%
    binary3      100%        0%           binary3      0%         100%   

USA
  Female                                Male
    variable       0          1           variable       0          1 
    binary1      25%         75%          binary1       100%        0%    
    binary2      0%          100%         binary2       100%        0%
    binary3      25%         75%          binary3       100%        0%    

Следует отметить, что у меня есть нагрузка двоичных переменных, пожалуйста, имейте это в виду. Это одна из моих главных проблем при ее реализации.

Будем весьма благодарны за любые советы или рекомендации!

Ответы [ 3 ]

2 голосов
/ 07 апреля 2020

Может быть, вы можете попробовать следующий базовый код R

dfout <- lapply(split(df,df$country), 
                function(v) lapply(split(v,v$sex),
                                   function(x) 100*data.frame(Var0 = 1-colMeans(x[-(1:2)]),Var1=colMeans(x[-(1:2)]))))

такой, что

> dfout
$germany
$germany$female
             Var0     Var1
binary1  66.66667 33.33333
binary2  66.66667 33.33333
binary3 100.00000  0.00000

$germany$male
        Var0 Var1
binary1   50   50
binary2   50   50
binary3    0  100


$USA
$USA$female
        Var0 Var1
binary1   25   75
binary2    0  100
binary3   25   75

$USA$male
        Var0 Var1
binary1  100    0
binary2  100    0
binary3  100    0
1 голос
/ 08 апреля 2020

Похоже, data.table::cube очень хорошо подходит для этой проблемы:

ans <- cube(melt(DT, id.vars=c("country", "sex")),
    .(c(0L, 1L), tabulate(value + 1L) / length(value)),
    c("country", "sex", "variable"))
ans[complete.cases(ans)]

вывод:

    country    sex variable V1        V2
 1: germany female  binary1  0 0.6666667
 2: germany female  binary1  1 0.3333333
 3: germany   male  binary1  0 0.5000000
 4: germany   male  binary1  1 0.5000000
 5:     USA female  binary1  0 0.2500000
 6:     USA female  binary1  1 0.7500000
 7:     USA   male  binary1  0 1.0000000
 8:     USA   male  binary1  1 1.0000000
 9: germany female  binary2  0 0.6666667
10: germany female  binary2  1 0.3333333
11: germany   male  binary2  0 0.5000000
12: germany   male  binary2  1 0.5000000
13:     USA female  binary2  0 0.0000000
14:     USA female  binary2  1 1.0000000
15:     USA   male  binary2  0 1.0000000
16:     USA   male  binary2  1 1.0000000
17: germany female  binary3  0 1.0000000
18: germany female  binary3  1 1.0000000
19: germany   male  binary3  0 0.0000000
20: germany   male  binary3  1 1.0000000
21:     USA female  binary3  0 0.2500000
22:     USA female  binary3  1 0.7500000
23:     USA   male  binary3  0 1.0000000
24:     USA   male  binary3  1 1.0000000
    country    sex variable V1        V2

данные:

library(data.table)
country <- c('germany','germany','germany','USA','USA','USA','USA','germany','germany','USA')
sex<-c('female','male','male','female','female','female','male','female','female','female')
binary1 <- c(1,1,0,1,0,1,0,0,0,1)
binary2 <- c(0,1,0,1,1,1,0,1,0,1)
binary3 <- c(0,1,1,1,0,1,0,0,0,1)
DT <- data.table(country,sex,binary1,binary2,binary3)
0 голосов
/ 08 апреля 2020

Оба из двух ответов выше выполнили свою работу, но я потратил немного времени на этот, возможно, самоизоляция стала дикой. Моей первой мыслью было использование janitor::tabyl как tabyl(df, sex, binary1, country) %>% adorn_percentages("row"), которое дает вам почти то, что вы хотите. К сожалению, он любит только одиночные переменные и не играет хорошо в цепочке purrr::map. Поэтому, используя инструменты basi c tidyverse, я написал пользовательскую функцию для создания тиббла, который будет хорошо играть с purrr, а затем, чтобы больше узнать о flextable, я использовал его, чтобы сделать вывод лучше. Обратите внимание, что ваши исходные данные не были фреймом данных, поэтому я изменил эту часть.

Преимущество моего решения IMHO в том, что оно очень расширяемо и модифицируемо.

library(tidyverse)
library(flextable)
#> 
#> Attaching package: 'flextable'
#> The following object is masked from 'package:purrr':
#> 
#>     compose
country <- c('germany','germany','germany','USA','USA','USA','USA','germany','germany','USA')
sex <- c('female','male','male','female','female','female','male','female','female','female')
binary1 <- c(1,1,0,1,0,1,0,0,0,1)
binary2 <- c(0,1,0,1,1,1,0,1,0,1)
binary3 <- c(0,1,1,1,0,1,0,0,0,1)

# make it a true dataframe
df <- as.data.frame(cbind(country,sex,binary1,binary2,binary3))

xtabs3 <- function(data,
                       x,
                       y,
                       z) {

  # internal helper function
  not_a_factor <- function(x){
    !is.factor(x)
  }

  # capture variable names
    xlab <- rlang::as_name(rlang::enquo(x))
    ylab <- rlang::as_name(rlang::enquo(y))
    zlab <- rlang::as_name(z)

  # create temp local dataframe 
    data <-
      dplyr::select(
        .data = data,
        x = {{ x }},
        y = {{ y }},
        z = {{ z }}
      )

  # calculate counts and percents 

  # x, y and z need to be a factor or ordered factor
  # also drop the unused levels of the factors and NAs
  data <- data %>%
    dplyr::mutate_if(.tbl = ., not_a_factor, as.factor) %>%
    dplyr::mutate_if(.tbl = ., is.factor, droplevels) %>%
    dplyr::filter_all(.tbl = ., all_vars(!is.na(.))) %>%
    dplyr::as_tibble(x = .)

  # convert the data into percentages; group by x, y, z
  # DO NOT Drop zeroes  
  df <-
    data %>%
    dplyr::group_by(.data = ., x, y, z, .drop = FALSE) %>%
    dplyr::summarize(.data = ., counts = n()) %>%
    dplyr::mutate(.data = ., perc = (counts / sum(counts)) * 100) %>%
    dplyr::ungroup(x = .) %>%
    rename(!!xlab := x, !!ylab := y, "level" := z)

    return(df)

}

# Make a list of all the binary variables we want to use
# best if it's a named list variables can be bare or quoted
fff <- alist(binary1 = binary1, binary2 = binary2, binary3 = binary3)
# fff <- alist(binary1, binary2, binary3)
# fff <- alist(binary1 = "binary1", binary2 = "binary2", binary3 = "binary3")

xxx <- purrr::map_dfr(.x = fff, ~ xtabs3(df, country, sex, .x), .id = "Which_binary")

xxx
#> # A tibble: 24 x 6
#>    Which_binary country sex    level counts  perc
#>    <chr>        <fct>   <fct>  <fct>  <int> <dbl>
#>  1 binary1      germany female 0          2  66.7
#>  2 binary1      germany female 1          1  33.3
#>  3 binary1      germany male   0          1  50  
#>  4 binary1      germany male   1          1  50  
#>  5 binary1      USA     female 0          1  25  
#>  6 binary1      USA     female 1          3  75  
#>  7 binary1      USA     male   0          1 100  
#>  8 binary1      USA     male   1          0   0  
#>  9 binary2      germany female 0          2  66.7
#> 10 binary2      germany female 1          1  33.3
#> # … with 14 more rows

myft <- flextable(xxx, col_keys = c("Which_binary", "country", "sex", "level", "perc"))
myft <- theme_vanilla(myft)
myft <- merge_v(myft, j = c("country", "sex", "Which_binary") )
myft <- autofit(myft)
myft <- colformat_num(x = myft, j = c("perc"), digits = 1, suffix = "%")
# reprex won't let me make an html table
plot(myft)

# myft

Создано в 2020-04-08 пакетом представительство (v0.3.0)

...