I need to recreate a function in R that creates a matrix out of a vector, given columns/rows and columnnames/rownames without using the characteristics of matrix().
I also need to add that if only a singular number is passed as a vector a matrix of given rows and columns is created with each entry being the passed number.
I tried using the dim() function but I'm not very familiar with it's usage. For example the function does not work if 1 is passed as the amount of rows or columns. My code looks the following:
matrix_by_hand <- function(v, nrow = NULL, ncol = NULL, row_names = NULL, col_names = NULL) {
if (is.null(nrow)) {
dim(v) <- (length(v)/ncol):ncol
} else if (is.null(ncol)) {
dim(v) <- (nrow:(length(v)/nrow))
}
if (!is.null(row_names)) {
rownames(v) <- row_names
}
if (!is.null(col_names)) {
colnames(v) <- col_names
}
print(v)
}
CodePudding user response:
Here is another option. Admittedly, its not as elegant as the other solution.
vec <- 1:9
matrix_by_hand <- function(v, nrow, ncol, row_names, col_names){
#check for errors
if(missing(nrow) & missing(ncol)) return("no dims")
else if(!missing(nrow) & !missing(ncol)) return("pick only one: ncol or nrow")
else if(!missing(nrow) && length(v) %% nrow != 0) return("inconsistent dims")
else if(!missing(ncol) && length(v) %% ncol != 0) return("inconsistent dims")
#define dim
if(!missing(ncol)){n <- ncol} else { n <- nrow}
#make the split
val <- split(v, f = rep(1:(length(v)/n), each = n))
#make the matrix
if(!missing(ncol)){mat <- Reduce(cbind, val)}else{mat <- Reduce(rbind, val)}
#define row and col names
if(!missing(row_names)){row.names(mat) <- row_names}
if(!missing(col_names)){colnames(mat) <- col_names}
return(mat)
}
matrix_by_hand(vec)
#> [1] "no dims"
matrix_by_hand(vec, nrow = 2)
#> [1] "inconsistent dims"
matrix_by_hand(vec, nrow = 3, ncol = 3)
#> [1] "pick only one: ncol or nrow"
matrix_by_hand(vec, nrow = 3)
#> [,1] [,2] [,3]
#> init 1 2 3
#> 4 5 6
#> 7 8 9
matrix_by_hand(vec, ncol = 3)
#> init
#> [1,] 1 4 7
#> [2,] 2 5 8
#> [3,] 3 6 9
matrix_by_hand(vec, nrow = 3, row_names = c("z", "y", "x"), col_names = c("a", "b", "c"))
#> a b c
#> z 1 2 3
#> y 4 5 6
#> x 7 8 9
CodePudding user response:
The following function should do what you need. You need to ensure that the row and column lengths are integers and that their product equals the length of the input vector. If not, the function should throw an error.
matrix_by_hand <- function(x, nrow = length(x), ncol = 1L,
row_names = NULL, col_names = NULL) {
nrow <- as.integer(nrow)
ncol <- as.integer(ncol)
stopifnot("Vector length must be ncol*nrow" = (ncol * nrow) %% length(x) == 0)
x <- rep(x, (ncol * nrow)/length(x))
dim(x) <- c(nrow, ncol)
dimnames(x) <- list(row_names, col_names)
return(x)
}
A few test cases:
matrix_by_hand(1:9, nrow = 3, ncol = 3, row_names = 1:3,
col_names = c("A", "B", "C"))
#> A B C
#> 1 1 4 7
#> 2 2 5 8
#> 3 3 6 9
matrix_by_hand(9, nrow = 3, ncol = 3, row_names = 1:3,
col_names = c("A", "B", "C"))
#> A B C
#> 1 9 9 9
#> 2 9 9 9
#> 3 9 9 9
matrix_by_hand(1:10, nrow = 3, ncol = 3, row_names = 1:3,
col_names = c("A", "B", "C"))
#> Error in matrix_by_hand(1:10, nrow = 3, ncol = 3, row_names = 1:3,
#> col_names = c("A", : Vector length must be ncol*nrow
matrix_by_hand(1:9, nrow = 3, ncol = 3)
#> [,1] [,2] [,3]
#> [1,] 1 4 7
#> [2,] 2 5 8
#> [3,] 3 6 9
Created on 2022-11-02 with reprex v2.0.2