Summarize Percentage by Group in R

Viewed 140

I'm looking to summarize my dataset by percentage of female and males by industry. I'm still learning R and have trouble figuring this out.

my data:

Industry Male Female
Art/Entertainment 100 500
Banking 600 100
Healthcare 53 65
Education 20 766
Military 47 96
Medicine 500 400
Law 500 500
Computer 200 144
Sales 420 69

Goal:

Industry Male Female F% M%
Art/Entertainment 100 500
Banking 600 100
Healthcare 53 65
Education 20 766
Military 47 96
Medicine 500 400
Law 500 500
Computer 200 144
Sales 420 69
4 Answers

1) proportions If your input is df1 (shown reproducibly in the Note at the end) then change the column names to those desired and convert it to a matrix m. Finally use proportions on that with a margin of 1 meaning row proportions -- 2 would mean column proportions. Note that we converted to a matrix in the first line because proportions requires that.

m <- as.matrix(setNames(df1[-1], c("%M", "%F")))
cbind(df1, 100 * proportions(m, 1))
##            Industry Male Female        %M       %F
## 1 Art/Entertainment  100    500 16.666667 83.33333
## 2           Banking  600    100 85.714286 14.28571
## 3        Healthcare   53     65 44.915254 55.08475
## ...snip...

2) rowSums Another approach is to divide df1[-1] by the rowSums giving the same reuslt.

cbind(df1, setNames(100 * df1[-1] / rowSums(df1[-1]), c("%M", "%F")))
##            Industry Male Female        %M       %F
## 1 Art/Entertainment  100    500 16.666667 83.33333
## 2           Banking  600    100 85.714286 14.28571
## 3        Healthcare   53     65 44.915254 55.08475
## ...snip...

3) dplyr Use across to make a copy of the columns with the indicated names and then multiply that by 100 and use c_across to divide by the sum of the columns

df1 %>%
  group_by(Industry) %>%
  mutate(100 * across(.names = "%{.col}") / sum(c_across())) %>%
  ungroup
## # A tibble: 9 x 5
##   Industry           Male Female `%Male` `%Female`
##   <chr>             <int>  <int>   <dbl>     <dbl>
## 1 Art/Entertainment   100    500   16.7       83.3
## 2 Banking             600    100   85.7       14.3
## 3 Healthcare           53     65   44.9       55.1
## ...snip...

4) transform This one is close to another answer but it does not overwrite the input:

transform(df1, 
  "%M" = 100 * Male / (Male + Female), 
  "%F" = 100 * Female / (Male + Female),  
  check.names = FALSE)
##            Industry Male Female        %M       %F
## 1 Art/Entertainment  100    500 16.666667 83.33333
## 2           Banking  600    100 85.714286 14.28571
## 3        Healthcare   53     65 44.915254 55.08475
## ...snip...

Note

Input in reproducible form:

df1 <- structure(list(Industry = c("Art/Entertainment", "Banking", "Healthcare", 
"Education", "Military", "Medicine", "Law", "Computer", "Sales"
), Male = c(100L, 600L, 53L, 20L, 47L, 500L, 500L, 200L, 420L
), Female = c(500L, 100L, 65L, 766L, 96L, 400L, 500L, 144L, 69L
)), class = "data.frame", row.names = c(NA, -9L))

If your data is named df, we can make columns for Male and Female percentages for like this:

df$Fpct <- df$Female / (df$Male + df$Female)
df$Mpct <- df$Male / (df$Male + df$Female)

Note, don't use % sign in your variable name.

there's an easy solution using library janitor meant for cross-tabulation purposes

library(janitor)

data %>% 
  adorn_totals(where = c("row","col")) %>% 
  adorn_percentages(denominator = "row") %>% 
  adorn_pct_formatting(digits = 0) %>% 
  adorn_ns(position = "front")

          Industry       Male     Female       Total
 Art/Entertainment  100 (17%)  500 (83%)  600 (100%)
           Banking  600 (86%)  100 (14%)  700 (100%)
        Healthcare   53 (45%)   65 (55%)  118 (100%)
         Education   20  (3%)  766 (97%)  786 (100%)
          Military   47 (33%)   96 (67%)  143 (100%)
          Medicine  500 (56%)  400 (44%)  900 (100%)
               Law  500 (50%)  500 (50%) 1000 (100%)
          Computer  200 (58%)  144 (42%)  344 (100%)
             Sales  420 (86%)   69 (14%)  489 (100%)
             Total 2440 (48%) 2640 (52%) 5080 (100%)

#OR

data %>% 
  adorn_percentages(denominator = "row") %>% 
  adorn_pct_formatting(digits = 2) %>% 
  adorn_ns(position = "front")

          Industry         Male       Female
 Art/Entertainment 100 (16.67%) 500 (83.33%)
           Banking 600 (85.71%) 100 (14.29%)
        Healthcare  53 (44.92%)  65 (55.08%)
         Education  20  (2.54%) 766 (97.46%)
          Military  47 (32.87%)  96 (67.13%)
          Medicine 500 (55.56%) 400 (44.44%)
               Law 500 (50.00%) 500 (50.00%)
          Computer 200 (58.14%) 144 (41.86%)
             Sales 420 (85.89%)  69 (14.11%)

data used

> data
           Industry Male Female
1 Art/Entertainment  100    500
2           Banking  600    100
3        Healthcare   53     65
4         Education   20    766
5          Military   47     96
6          Medicine  500    400
7               Law  500    500
8          Computer  200    144
9             Sales  420     69

You can use group_by and then create two new columns with F% and M%

Maybe you can use this:

library(dplyr)
df %>% group_by(Industry)  %>% mutate(F_prec=Female/(Male+Female), M_prec=Male/(Male+Female))
Related