Home > Mobile >  Summarize proportion of categorical variables and assign dominant categorical variable per group
Summarize proportion of categorical variables and assign dominant categorical variable per group

Time:12-22

I have a set of sample plots from a map with polygon features (A, B, and C) and a raster of forest type (Cold, Warm, and Hot).

Plot    Polygon Forest
1       A       Cold
2       A       Cold
3       A       Cold
4       A       Warm
5       B       Cold
6       B       Cold
7       C       Cold
8       C       Warm
9       C       Hot
10      C       Hot

I want to summarize the proportion of each forest type by polygon and also identify the dominant forest type in each polygon. For example:

Polygon Cold    Warm    Hot   Forest_dominant
A       0.75    0.25    0     Cold
B       1       0       0     Cold
C       0.25    0.25    0.5   Hot

CodePudding user response:

It's a bit convoluted, but perhaps:

library(tidyverse)

df <- structure(list(Plot = 1:10, Polygon = c("A", "A", "A", "A", "B", 
                                              "B", "C", "C", "C", "C"), Forest = c("Cold", "Cold", "Cold", 
                                                                                   "Warm", "Cold", "Cold", "Cold", "Warm", "Hot", "Hot")), class = "data.frame", row.names = c(NA, 
                                                                                                                                                                               -10L))
df %>%
  group_by(Polygon, Forest) %>%
  summarise(n = n()) %>%
  mutate(n = n / sum(n)) %>%
  group_by(Polygon) %>%
  arrange(Polygon, -n) %>%
  mutate(Forest_dominant = first(Forest)) %>%
  pivot_wider(names_from = Forest, values_from = n, values_fill = 0) %>%
  relocate(Forest_dominant, .after = last_col())
#> `summarise()` has grouped output by 'Polygon'. You can override using the `.groups` argument.
#> # A tibble: 3 × 5
#> # Groups:   Polygon [3]
#>   Polygon  Cold  Warm   Hot Forest_dominant
#>   <chr>   <dbl> <dbl> <dbl> <chr>          
#> 1 A        0.75  0.25   0   Cold           
#> 2 B        1     0      0   Cold           
#> 3 C        0.25  0.25   0.5 Hot

Created on 2021-12-22 by the reprex package (v2.0.1)

CodePudding user response:

We first could calculate the proportion within each group and then pivot_wider

library(dplyr)
library(tidyr)

df %>%
  group_by(Polygon, Forest) %>% 
  summarise(n = n()) %>% 
  mutate(proportion = n/ sum(n),  
         Forest_dominant = max(proportion), .keep="unused") %>% 
  pivot_wider(
    names_from = Forest,
    values_from = proportion,
    values_fill = 0
  )
   Polygon Forest_dominant  Cold  Warm   Hot
  <chr>   <chr>           <dbl> <dbl> <dbl>
1 A       Cold             0.75  0.25   0  
2 B       Cold             1     0      0  
3 C       Hot              0.25  0.25   0.5

CodePudding user response:

A base R option

reshape(
  unique(
    transform(
      df,
      prop = ave(Forest, Polygon, FUN = function(x) table(x)[x] / length(x)),
      Forest_dominant = ave(Forest, Polygon, FUN = function(x) names(which.max(table(x))))
    )
  ),
  direction = "wide",
  idvar = c("Polygon", "Forest_dominant"),
  timevar = "Forest"
)

gives

  Polygon Forest_dominant prop.Cold prop.Warm prop.Hot
1       A            Cold      0.75      0.25     <NA>
5       B            Cold         1      <NA>     <NA>
7       C             Hot      0.25      0.25      0.5

or a data.table option

dcast(
  setDT(df)[
    ,
    .(cnt = .N), .(Polygon, Forest)
  ][
    ,
    `:=`(prop = proportions(cnt), Forest_dominant = Forest[which.max(cnt)]),
    Polygon
  ],
  Polygon   Forest_dominant ~ Forest,
  value.var = "prop",
  fill = 0
)

gives

   Polygon Forest_dominant Cold Hot Warm
1:       A            Cold 0.75 0.0 0.25
2:       B            Cold 1.00 0.0 0.00
3:       C             Hot 0.25 0.5 0.25

Data

> dput(df)
structure(list(Polygon = c("A", "A", "A", "A", "B", "B", "C", 
"C", "C", "C"), Forest = c("Cold", "Cold", "Cold", "Warm", "Cold",
"Cold", "Cold", "Warm", "Hot", "Hot")), row.names = c(NA, -10L
), class = "data.frame")

CodePudding user response:

library(dplyr, warn.conflicts = FALSE)

df %>% 
  group_by(Polygon) %>% 
  summarise(prop.table(table(Forest)) %>% as.list %>% as_tibble) %>% 
  mutate(
    across(-1, coalesce, 0),
    Forest_dominant = across(-1) %>% {names(.)[max.col(.)]}
  )
#> # A tibble: 3 × 5
#>   Polygon  Cold  Warm   Hot Forest_dominant
#>   <chr>   <dbl> <dbl> <dbl> <chr>          
#> 1 A        0.75  0.25   0   Cold           
#> 2 B        1     0      0   Cold           
#> 3 C        0.25  0.25   0.5 Hot

Created on 2021-12-21 by the reprex package (v2.0.1)

  •  Tags:  
  • r
  • Related