diff --git a/dev/tag-message b/dev/tag-message
index 01c6fcd62206f468ab1fbbcaca18a554ca99f0bc..54af56c9412adf40c628eb6d913b9994e73160dc 100644
--- a/dev/tag-message
+++ b/dev/tag-message
@@ -1,18 +1,16 @@
-so.ii Version: 1.0.26.0
+so.ii Version: 1.0.27.0
 
 0 errors ✔ | 0 warnings ✔ | 0 notes ✔
 
 **Note de version**
-    * modification de map_so_ii
-    * nouvelle fonction interne calculate_terra_xy
+    * ajout de aggregate_wide
+    * ajout de add_margin
 
 **Détails**
-    * La librairie terra a changé sa façon de faire la barre d'échelle avec
-    sbar à partir de la version 1.7, de façon incompatible avec l'usage interne.
-    Une nouvelle fonction non exportée calculate_terra_xy est créée pour ne plus
-    dépendre des fonctionnalités de terra::sbar qui posent problème, et ne
-    permettaient plus de faire un positionnement par mots clés.
+    * aggregate_wide permet de renvoyer directement une matrice quand un
+    aggregate est réalisé sur 2 dimensions.
+    * add_margin permet d'utiliser addmargins directement sur un data.frame
 
-# git tag -a v1.0.25.0 -F dev/tag-message
+# git tag -a v1.0.27.0 -F dev/tag-message
 # git push --tags
 
diff --git a/so.ii/DESCRIPTION b/so.ii/DESCRIPTION
index db2020b5585b6b847e7c25b997e5a37b96c7a218..cf42619b4b9e1cf3a6d226794b5b12a6fc80d348 100644
--- a/so.ii/DESCRIPTION
+++ b/so.ii/DESCRIPTION
@@ -1,6 +1,6 @@
 Package: so.ii
 Title: Utilities very useful to share within so_ii team
-Version: 1.0.26.0
+Version: 1.0.27.0
 Authors@R:
     c(
         person(given = "Frédéric",
@@ -33,5 +33,5 @@ Suggests:
     rmarkdown,
     testthat
 Roxygen: list(markdown = TRUE)
-RoxygenNote: 7.2.2
+RoxygenNote: 7.2.3
 VignetteBuilder: knitr
diff --git a/so.ii/NAMESPACE b/so.ii/NAMESPACE
index d7b419e623fd8ec52b0c290c93525b98f6f4374a..437c8b8b3c3f92f0fe5372e1f5558a03c431c0c0 100644
--- a/so.ii/NAMESPACE
+++ b/so.ii/NAMESPACE
@@ -1,6 +1,8 @@
 # Generated by roxygen2: do not edit by hand
 
 export(add_inset)
+export(add_margin)
+export(aggregate_wide)
 export(current_version)
 export(estimate_catnat_freq)
 export(format_presence)
diff --git a/so.ii/R/add_margin.R b/so.ii/R/add_margin.R
new file mode 100644
index 0000000000000000000000000000000000000000..7c2de8212403c7fff62b6bac28ea26731f9374d6
--- /dev/null
+++ b/so.ii/R/add_margin.R
@@ -0,0 +1,65 @@
+#' @title Replace addmargins with treatment for data.frame
+#' 
+#' @param x either a matrix, an array or a data.frame
+#' @param name vector, if x is a data.frame, gives the poistion of what should
+#' be considered as names (and so not pass to addmargins).
+#' @param output character, gives the type of output, either as.is or a
+#' data.frame.
+#' @param ... arguments that will be used for addmargins
+#'
+#' @return A table, array or data.frame with margins added.
+#'
+#' @export
+#'
+#' @encoding UTF-8
+#'
+#' @author Frédéric Grelot
+#' 
+#' @examples
+#'
+#' add_margin(head(iris), 5)
+#' add_margin(head(iris), 5, "data.frame")
+#' add_margin(head(iris), 5, FUN = list("total" = sum))
+#' 
+#' x = data.frame(
+#'     gender = sample(c("H", "F"), 20, replace = TRUE),
+#'     region = sample(c("east", "west", "south", "north"), 20, replace = TRUE),
+#'     wealth = round(runif(20) * 10000),
+#'     size = round(runif(20) * 50 + 150)
+#' )
+#' add_margin(head(x), 1:2, margin = 1, FUN = list(mean = mean))
+ 
+add_margin = function(
+    x,
+    name,
+    output = c("normal", "data.frame"),
+    ...
+) {
+    output = match.arg(output)
+
+    if (is.data.frame(x)) {
+        if (missing(name)) name = 1
+        if (is.character(name)) name = grep(name, names(x))
+        x_name = x[name]
+        if (length(name) > 0) x = x[-name]
+        x = structure(
+            as.matrix(x),
+            dimnames = list(
+                apply(as.matrix(x_name), 1, paste, collapse = "_"),
+                colnames(x)
+            )
+        )
+    }
+
+    if (!is.array(x) && ! is.table(x)) {
+        stop("x should be something addmargins will expect at this stage!")
+    }
+
+    result = stats::addmargins(x, ...)
+
+    if (output == "data.frame") {
+        result = cbind(name = rownames(result), as.data.frame(result))
+    }
+
+    return(result)
+}
\ No newline at end of file
diff --git a/so.ii/R/aggregate_wide.R b/so.ii/R/aggregate_wide.R
new file mode 100644
index 0000000000000000000000000000000000000000..182761b9843edb5ed7a5b71aeb3497725241a071
--- /dev/null
+++ b/so.ii/R/aggregate_wide.R
@@ -0,0 +1,52 @@
+#' @title A wrapper to aggregate that gives a wide view of results
+#' 
+#' @param x data.frame 
+#' @param what vector of length
+#' @param fun function
+#'
+#' @return A matrix of result
+#' @export
+#'
+#' @encoding UTF-8
+#'
+#' @author Frédéric Grelot
+#' 
+#' @examples
+#' x = data.frame(
+#'     gender = sample(c("H", "F"), 20, replace = TRUE),
+#'     region = sample(c("east", "west", "south", "north"), 20, replace = TRUE),
+#'     wealth = round(runif(20) * 10000),
+#'     size = round(runif(20) * 50 + 150)
+#' )
+#' aggregate_wide(x)
+#' aggregate_wide(x, c("region", "gender", "wealth"))
+#' aggregate_wide(x, c("region", "gender", "size"))
+#' aggregate_wide(x, c("region", "gender", "wealth"), mean)
+
+aggregate_wide = function(x, what, fun = sum) {
+    if (missing(what)) what = 1:3
+    if (!is.vector(what) && length(what) == 3) {
+        stop("what should be a vector of length 3")
+    }
+
+    result = stats::aggregate(
+        x[what[3]],
+        x[c(what[1], what[2])],
+        fun,
+        drop = FALSE,
+        na.rm = TRUE
+    )
+    result = stats::reshape(
+        result,
+        direction = "wide",
+        idvar = names(result)[1],
+        timevar = names(result)[2]
+    )
+    result = structure(
+        as.matrix(result[-1]),
+        dimnames = list(
+            result[[1]],
+            gsub("^.*[.]", "", names(result)[-1]))
+    )
+    return(result)
+}
diff --git a/so.ii/man/add_margin.Rd b/so.ii/man/add_margin.Rd
new file mode 100644
index 0000000000000000000000000000000000000000..4a8c76277b2c7d073fd4f8a6949e309aace9bff2
--- /dev/null
+++ b/so.ii/man/add_margin.Rd
@@ -0,0 +1,43 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add_margin.R
+\encoding{UTF-8}
+\name{add_margin}
+\alias{add_margin}
+\title{Replace addmargins with treatment for data.frame}
+\usage{
+add_margin(x, name, output = c("normal", "data.frame"), ...)
+}
+\arguments{
+\item{x}{either a matrix, an array or a data.frame}
+
+\item{name}{vector, if x is a data.frame, gives the poistion of what should
+be considered as names (and so not pass to addmargins).}
+
+\item{output}{character, gives the type of output, either as.is or a
+data.frame.}
+
+\item{...}{arguments that will be used for addmargins}
+}
+\value{
+A table, array or data.frame with margins added.
+}
+\description{
+Replace addmargins with treatment for data.frame
+}
+\examples{
+
+add_margin(head(iris), 5)
+add_margin(head(iris), 5, "data.frame")
+add_margin(head(iris), 5, FUN = list("total" = sum))
+
+x = data.frame(
+    gender = sample(c("H", "F"), 20, replace = TRUE),
+    region = sample(c("east", "west", "south", "north"), 20, replace = TRUE),
+    wealth = round(runif(20) * 10000),
+    size = round(runif(20) * 50 + 150)
+)
+add_margin(head(x), 1:2, margin = 1, FUN = list(mean = mean))
+}
+\author{
+Frédéric Grelot
+}
diff --git a/so.ii/man/aggregate_wide.Rd b/so.ii/man/aggregate_wide.Rd
new file mode 100644
index 0000000000000000000000000000000000000000..046604818ca24eb0a73969491b77b5a0609cc1ca
--- /dev/null
+++ b/so.ii/man/aggregate_wide.Rd
@@ -0,0 +1,37 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/aggregate_wide.R
+\encoding{UTF-8}
+\name{aggregate_wide}
+\alias{aggregate_wide}
+\title{A wrapper to aggregate that gives a wide view of results}
+\usage{
+aggregate_wide(x, what, fun = sum)
+}
+\arguments{
+\item{x}{data.frame}
+
+\item{what}{vector of length}
+
+\item{fun}{function}
+}
+\value{
+A matrix of result
+}
+\description{
+A wrapper to aggregate that gives a wide view of results
+}
+\examples{
+x = data.frame(
+    gender = sample(c("H", "F"), 20, replace = TRUE),
+    region = sample(c("east", "west", "south", "north"), 20, replace = TRUE),
+    wealth = round(runif(20) * 10000),
+    size = round(runif(20) * 50 + 150)
+)
+aggregate_wide(x)
+aggregate_wide(x, c("region", "gender", "wealth"))
+aggregate_wide(x, c("region", "gender", "size"))
+aggregate_wide(x, c("region", "gender", "wealth"), mean)
+}
+\author{
+Frédéric Grelot
+}
diff --git a/so.ii/man/kable_units.Rd b/so.ii/man/kable_units.Rd
index 8471709c6fc3f8a793df281b4a3256e52f7cebc6..91a5f5f5b96cf5245d1da7d671217c75795b121f 100644
--- a/so.ii/man/kable_units.Rd
+++ b/so.ii/man/kable_units.Rd
@@ -1,5 +1,5 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/kable_units.r
+% Please edit documentation in R/kable_units.R
 \encoding{UTF-8}
 \name{kable_units}
 \alias{kable_units}
diff --git a/so.ii/man/map_so_ii.Rd b/so.ii/man/map_so_ii.Rd
index b7e4a854e8c2f73ee4882978b926f99c27234de3..3db0d77dd008e5a1f8037048ad0f88ae45138b07 100644
--- a/so.ii/man/map_so_ii.Rd
+++ b/so.ii/man/map_so_ii.Rd
@@ -1,5 +1,5 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/map_so_ii.r
+% Please edit documentation in R/map_so_ii.R
 \encoding{UTF-8}
 \name{map_so_ii}
 \alias{map_so_ii}
diff --git a/so.ii/man/plot_legend.Rd b/so.ii/man/plot_legend.Rd
index ea96e6e74a5adc4f46013a16bd2dbee2178fbf19..2edf1313d6dad80ae0a3ce5b06d86985504f9828 100644
--- a/so.ii/man/plot_legend.Rd
+++ b/so.ii/man/plot_legend.Rd
@@ -1,5 +1,5 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/plot_legend.r
+% Please edit documentation in R/plot_legend.R
 \encoding{UTF-8}
 \name{plot_legend}
 \alias{plot_legend}