D1X
D1X

Reputation: 5464

Avoid simplification in apply()

Pretty simple question.

Is there a way to avoid simplification in apply()?

I need to do it because I have an apply which can sometimes simplify (and it does it) and sometimes not, thus creating different data structures depending on the input, so I want to avoid it altogether.

I need either something similar to SIMPLIFY = FALSE in mapply() or a mechanism to control output as the one in vapply().


Simple reproducible example:

mimat <- matrix(c(1,2,3,4,5,6), nrow = 2)
mimat2 <- matrix(c(3,2,3,4,5,6), nrow = 2)

apply(mimat, MARGIN = 2, function(x) { 
                              if (is.element(el = 1, x)) return(c(0,1))
                              else return(c(1,2,3))
      })

If the apply() is applied to mimat it outputs a list, whereas if it is applied to mimat2 it outputs a matrix.

Upvotes: 5

Views: 1510

Answers (2)

Martin Smith
Martin Smith

Reputation: 4077

R4.1.0 added a simplify argument to apply() on 2021-03-06.

With older versions of R, the best option is probably

lapply(seq_len(dim(minat)[2]), YourFunction)

Upvotes: 3

Anoushiravan R
Anoushiravan R

Reputation: 21938

I think for your purpose functions from purrr package might be more suitable as the output is almost known beforehand. And if they couldn't match the output to the data structure they are supposed to return, they throw and error (or die trying as the documentation put it).

For example map function always returns a list, whereas its variation map_dbl, map_lgl and etc each returns a vector of the indicated type.

There is another function called modify in this family that preserves the structure of the input data (row numbers and col numbers) and don't alter the data structure, however it is said in the documentation that

Since the transformation can alter the structure of the input; it's your responsibility to ensure that the transformation produces a valid output. For example, if you're modifying a data frame, .f must preserve the length of the input.

In your first example when you apply your anonymous function to mimat the result has different lengths so it is not possible to return a matrix or a data frame as I can try it with map to get the same result whereas modify fails to preserve the data structure of the input:

library(purrr)

map(as.data.frame(mimat), function(x) { 
  if (is.element(el = 1, x)) return(c(0,1))
  else return(c(1,2,3))
})

$V1
[1] 0 1

$V2
[1] 1 2 3

$V3
[1] 1 2 3

modify(as.data.frame(mimat), function(x) { 
  if (is.element(el = 1, x)) return(c(0,1))
  else return(c(1,2,3))
})

> Error in `[[<-.data.frame`(`*tmp*`, i, value = c(1, 2, 3)) :   
> replacement has 3 rows, data has 2

But in case of your second matrix mimat2 the output of the function has rows of equal lengths so it is possible to make use of a map variation called map_dfc to col bind the output. As the modify again fails cause the output data structure is different from the output:

map_dfc(as.data.frame(mimat2), function(x) { 
  if (is.element(el = 1, x)) return(c(0,1))
  else return(c(1,2,3))
})

# A tibble: 3 x 3
     V1    V2    V3
  <dbl> <dbl> <dbl>
1     1     1     1
2     2     2     2
3     3     3     3

modify(as.data.frame(mimat2), function(x) { 
  if (is.element(el = 1, x)) return(c(0,1))
  else return(c(1,2,3))
})

Error in `[[<-.data.frame`(`*tmp*`, i, value = c(1, 2, 3)) : 
  replacement has 3 rows, data has 2

I had to transform them to data frame as otherwise they are considered as a vector and the function is applied on every element instead of just columns. This brief explanation may not get you to what you wanted but I hope it just serves as an introduction to some other functions with fixed outputs. In a nutshell it only boils down to the function you apply on your data structure that has to transform the data in a way that makes it possible to preserve the data structure.

Upvotes: 0

Related Questions