Let’s look examples at this unappreciated function.

A sample inventory table:

n <- 20
{
  set.seed(153229)
  df <- data.frame(
    date = sort(sample(seq(as.Date("2019-02-27"), as.Date("2019-03-01"), by = "day"), n, replace = TRUE)),
    category = sample(LETTERS[seq(4L)], n, replace = TRUE),
    item = replicate(n, paste(sample(c(letters, LETTERS), 4L), collapse = "")),
    stock = sample(1:100, n, replace = TRUE),
    sold = sample(c(TRUE, FALSE), n, replace = TRUE)
  )
}

The whole table looks like this:

##          date category item stock  sold
## 1  2019-02-27        C ldkv    15 FALSE
## 2  2019-02-27        A xUGg    80 FALSE
## 3  2019-02-27        D oJed    72 FALSE
## 4  2019-02-27        A zwlZ    28  TRUE
## 5  2019-02-27        A CMRP    38  TRUE
## 6  2019-02-27        D qHlE    12 FALSE
## 7  2019-02-28        A TQaD     3  TRUE
## 8  2019-02-28        D lmct    13  TRUE
## 9  2019-02-28        C yVWr    10  TRUE
## 10 2019-02-28        B thlS    40 FALSE
## 11 2019-02-28        D pagh    77 FALSE
## 12 2019-02-28        A ykBn    10 FALSE
## 13 2019-02-28        D vPmV    43 FALSE
## 14 2019-02-28        B mGAu    50  TRUE
## 15 2019-03-01        A aqGw    48 FALSE
## 16 2019-03-01        C SljP    15  TRUE
## 17 2019-03-01        A Uema    17  TRUE
## 18 2019-03-01        D WTGS    76 FALSE
## 19 2019-03-01        D JFlr    11 FALSE
## 20 2019-03-01        A kehY     8 FALSE

Additional information about the table:

str(df)

## 'data.frame':    20 obs. of  5 variables:
##  $ date    : Date, format: "2019-02-27" "2019-02-27" ...
##  $ category: Factor w/ 4 levels "A","B","C","D": 3 1 4 1 1 4 1 4 3 2 ...
##  $ item    : Factor w/ 20 levels "aqGw","CMRP",..: 5 17 8 20 2 10 13 6 19 12 ...
##  $ stock   : int  15 80 72 28 38 12 3 13 10 40 ...
##  $ sold    : logi  FALSE FALSE FALSE TRUE TRUE FALSE ...

summary(df)

##       date            category      item        stock      
##  Min.   :2019-02-27   A:8      aqGw   : 1   Min.   : 3.00  
##  1st Qu.:2019-02-27   B:2      CMRP   : 1   1st Qu.:11.75  
##  Median :2019-02-28   C:3      JFlr   : 1   Median :22.50  
##  Mean   :2019-02-28   D:7      kehY   : 1   Mean   :33.30  
##  3rd Qu.:2019-03-01            ldkv   : 1   3rd Qu.:48.50  
##  Max.   :2019-03-01            lmct   : 1   Max.   :80.00  
##                                (Other):14                  
##     sold        
##  Mode :logical  
##  FALSE:12       
##  TRUE :8        
##                 
##                 
##                 
## 

One can use formula in aggregate. Creating a formula programmaticaly is also possible with a bit help from paste() and as.formula():

d <- aggregate(stock ~ category + date + sold, df, sum)

##   category       date  sold stock
## 1        A 2019-02-27 FALSE    80
## 2        C 2019-02-27 FALSE    15
## 3        D 2019-02-27 FALSE    84
## 4        A 2019-02-28 FALSE    10
## 5        B 2019-02-28 FALSE    40
## 6        D 2019-02-28 FALSE   120

Here don’t include the variable that you want to sort out e.g. we sort percentages by category here so don’t include inside ave().

d.t <- transform(d, percent = ave(stock, date, sold, FUN = function(x) {
  paste0(format(x/sum(x)*100, decimal.mark = ",", digits = 3), "%")
}))

##   category       date  sold stock
## 1        A 2019-02-27 FALSE    80
## 2        C 2019-02-27 FALSE    15
## 3        D 2019-02-27 FALSE    84
## 4        A 2019-02-28 FALSE    10
## 5        B 2019-02-28 FALSE    40
## 6        D 2019-02-28 FALSE   120

Using multiple function with aggregate():

aggregate(stock ~ category, df, function(x) c(mean = mean(x), sum = sum(x)))

##   category stock.mean stock.sum
## 1        A   29.00000 232.00000
## 2        B   45.00000  90.00000
## 3        C   13.33333  40.00000
## 4        D   43.42857 304.00000