Min-max normalization in R, setting groups of min and max based on another column

Viewed 1165

Using R, I am trying to min-max normalize a column, but instead of using the min and max of all the column values, I need to set min and max by groups that are determined by another column.

Please see this example:

x <- c(0, 0.5, 1, 2.5, 0.2, 0.3, 0.5, 0,0,0.1, 0.7)
y <- c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3)

df <- data.frame (x, y)

df

For y=1, min(x) = 0, and max(x) = 2.5. For y=2, min(x) = 0.2, and max(x) = 0.5, and so forth.

Based on this grouped min and max, normalization is performed.

I found a similar question for Python, but it didnt help me much: Normalize a column of dataframe using min max normalization based on groupby of another column

3 Answers
library(tidyverse)

df %>%
  group_by(y) %>%
  mutate(xnorm = (x - min(x)) / (max(x) - min(x))) %>%
  ungroup()

Output:

# A tibble: 11 x 3
       x     y xnorm
   <dbl> <dbl> <dbl>
 1   0       1 0    
 2   0.5     1 0.2  
 3   1       1 0.4  
 4   2.5     1 1    
 5   0.2     2 0    
 6   0.3     2 0.333
 7   0.5     2 1    
 8   0       3 0    
 9   0       3 0    
10   0.1     3 0.143
11   0.7     3 1 

Or, in the mutate() statement, you could put xnorm = scales::rescale(x)

I am not sure if you need something like below

dfout <- within(df,xnorm <- ave(x,y,FUN = function(v) (v-min(v))/diff(range(v))))

such that

> dfout
     x y     xnorm
1  0.0 1 0.0000000
2  0.5 1 0.2000000
3  1.0 1 0.4000000
4  2.5 1 1.0000000
5  0.2 2 0.0000000
6  0.3 2 0.3333333
7  0.5 2 1.0000000
8  0.0 3 0.0000000
9  0.0 3 0.0000000
10 0.1 3 0.1428571
11 0.7 3 1.0000000

You can use function aggregate

aggregate(x, list(y), min)
  Group.1   x
1       1 0.0
2       2 0.2
3       3 0.0
aggregate(x, list(y), max)
  Group.1   x
1       1 2.5
2       2 0.5
3       3 0.7

# You can create your own function like this
myFun <- function (u) {
    c(min(u), mean(u), max(u))
} 
# and pass myFun to aggregate
aggregate(x, list(y), myFun)
  Group.1       x.1       x.2       x.3
1       1 0.0000000 1.0000000 2.5000000
2       2 0.2000000 0.3333333 0.5000000
3       3 0.0000000 0.2000000 0.7000000

# alternative is "by" different output format
by(x, list(y), myFun)
Related