Consistent method to add id column to list elements - r

I am having trouble using mapply to add ID columns to lists (within a larger function).
With most lists the code below works fine...
m1 <- matrix(data = 1:32, nrow = 8, ncol = 4)
m2 <- matrix(data = 1:8, nrow = 2, ncol = 4)
m3 <- matrix(data = 1:16, nrow = 4, ncol = 4)
m4 <- matrix(data = 1:4, nrow = 1, ncol = 4)
colnames(m1) <- colnames(m2) <- colnames(m3) <- colnames(m4) <- LETTERS[1:4]
a <- list(m1, m2, m3, m4)
mapply(cbind, a, m = 1:4)
# [[1]]
# A B C D m
# [1,] 1 9 17 25 1
# [2,] 2 10 18 26 1
# [3,] 3 11 19 27 1
# [4,] 4 12 20 28 1
# [5,] 5 13 21 29 1
# [6,] 6 14 22 30 1
# [7,] 7 15 23 31 1
# [8,] 8 16 24 32 1
#
# [[2]]
# A B C D m
# [1,] 1 3 5 7 2
# [2,] 2 4 6 8 2
#
# [[3]]
# A B C D m
# [1,] 1 5 9 13 3
# [2,] 2 6 10 14 3
# [3,] 3 7 11 15 3
# [4,] 4 8 12 16 3
#
# [[4]]
# A B C D m
# [1,] 1 2 3 4 4
but when I have lists that contain matrices of the same dimensions a list is no longer returned...
a <- list(m2, m2, m2, m2)
mapply(cbind, a, m = 2)
# [,1] [,2] [,3] [,4]
# [1,] 1 1 1 1
# [2,] 2 2 2 2
# [3,] 3 3 3 3
# [4,] 4 4 4 4
# [5,] 5 5 5 5
# [6,] 6 6 6 6
# [7,] 7 7 7 7
# [8,] 8 8 8 8
# [9,] 2 2 2 2
# [10,] 2 2 2 2
I cannot find any explanation as to why this is happening or a possible workaround (where the output would be a list in a similar format to first example above)?

That is exactly the difference between mapply and Map, mapply by default has simplify = TRUE argument which means
SIMPLIFY - logical or character string; attempt to reduce the result to a vector, matrix or higher dimensional array; see the simplify argument of sapply.
Hence, in this case, to get output same as the first example we should use Map
Map(cbind, a, m = 2)
#[[1]]
# A B C D m
#[1,] 1 3 5 7 2
#[2,] 2 4 6 8 2
#[[2]]
# A B C D m
#[1,] 1 3 5 7 2
#[2,] 2 4 6 8 2
#[[3]]
# A B C D m
#[1,] 1 3 5 7 2
#[2,] 2 4 6 8 2
#[[4]]
# A B C D m
#[1,] 1 3 5 7 2
#[2,] 2 4 6 8 2

Related

How can I name my matrix using dimnames()? [duplicate]

This question already has answers here:
3 Dimensional Array Names in R
(2 answers)
Closed 6 years ago.
I have a existing array that I made and I want to name the dimensions of this array. I can't use the dimnames= argument of array() because I need to make this array with a different function. I need to rename the dimensions with something similar to this names(my.array)<-my.names.
Thanks for the help (and I'm new to this if you can't already tell)
Use dimnames(x) <- list(d1names, d2names, ...) where d1names, d2names and so forth are character vectors whose lengths match the lengths of your dimensions.
If your array is bidimensional (a matrix), you can use rownames(x) <- d1names and colnames(x) <- d2names instead.
Example:
> A <- outer(outer(1:3,1:4),1:2)
> A
, , 1
[,1] [,2] [,3] [,4]
[1,] 1 2 3 4
[2,] 2 4 6 8
[3,] 3 6 9 12
, , 2
[,1] [,2] [,3] [,4]
[1,] 2 4 6 8
[2,] 4 8 12 16
[3,] 6 12 18 24
> dimnames(A)
NULL
> dimnames(A) <- list(LETTERS[1:3],LETTERS[1:4],LETTERS[1:2])
> A
, , A
A B C D
A 1 2 3 4
B 2 4 6 8
C 3 6 9 12
, , B
A B C D
A 2 4 6 8
B 4 8 12 16
C 6 12 18 24
Example with matrix:
> B <- matrix(1:12,3,4)
> B
[,1] [,2] [,3] [,4]
[1,] 1 4 7 10
[2,] 2 5 8 11
[3,] 3 6 9 12
> rownames(B) <- letters[1:3]
> B
[,1] [,2] [,3] [,4]
a 1 4 7 10
b 2 5 8 11
c 3 6 9 12
> colnames(B) <- LETTERS[1:4]
> B
A B C D
a 1 4 7 10
b 2 5 8 11
c 3 6 9 12
you can still use dimnames<-
For example:
someArray <- array(1:30, dim=c(2, 3, 5))
dimnames(someArray) <- list(c("Hello", "World"), LETTERS[6:8], letters[1:5])
someArray
# , , a
# F G H
# Hello 1 3 5
# World 2 4 6
# , , b
# F G H
# Hello 7 9 11
# World 8 10 12
# , , c
# F G H
# Hello 13 15 17
# World 14 16 18
# , , d
# F G H
# Hello 19 21 23
# World 20 22 24
# , , e
# F G H
# Hello 25 27 29
# World 26 28 30

save a loop while an matriz or a data frame

I want to save a while in an matrix or in a data frame, in such a way that it places me in an orderly way
i <- 15#year
pon<-list()
while (i < 63) {
pon[i] <-cumprod( vlookup(i:62,Tabla_de_mortalidad_css,4))
i = i+1}
this is my command that i am doing
I want you to print something like that: for example
v1 v2 v3
1
2 1
3 2 1
4 3 2
. . .
. . .
. . .
v1, v2, v3 are my variabl
Do you need something like this?
n <- 1:63
mat <- cbind(1:63, sapply(1:3, function(x) c(rep(NA, x), head(n, -x))))
mat
# [,1] [,2] [,3] [,4]
# [1,] 1 NA NA NA
# [2,] 2 1 NA NA
# [3,] 3 2 1 NA
# [4,] 4 3 2 1
# [5,] 5 4 3 2
# [6,] 6 5 4 3
# [7,] 7 6 5 4
#...
#...
use tidyverse
library(tidyverse)
n <- 1:10
Lag <- 1:3
df <- data.frame(n = n)
bind_cols(df, map_dfc(Lag, ~transmute(df, !!paste0("Lag", .x) := lag(n, n = .x))))
#> n Lag1 Lag2 Lag3
#> 1 1 NA NA NA
#> 2 2 1 NA NA
#> 3 3 2 1 NA
#> 4 4 3 2 1
#> 5 5 4 3 2
#> 6 6 5 4 3
#> 7 7 6 5 4
#> 8 8 7 6 5
#> 9 9 8 7 6
#> 10 10 9 8 7
Created on 2020-12-11 by the reprex package (v0.3.0)
We can do this easily with shift from data.table
library(data.table)
do.call(cbind, shift(1:10, n = 1:3))
-output
# [,1] [,2] [,3]
# [1,] NA NA NA
# [2,] 1 NA NA
# [3,] 2 1 NA
# [4,] 3 2 1
# [5,] 4 3 2
# [6,] 5 4 3
# [7,] 6 5 4
# [8,] 7 6 5
# [9,] 8 7 6
#[10,] 9 8 7

Get all combination of n vectors taken 2 vectors at a time

I have three vectors
a = 1:5
b = 6:10
c = 11:15
I want to get data.frames by taking all combinations of a, b, and c two at a time into a list. Below is my desired output.
list(cbind(a,b), cbind(a,c), cbind(b,c))
[[1]]
a b
[1,] 1 6
[2,] 2 7
[3,] 3 8
[4,] 4 9
[5,] 5 10
[[2]]
a c
[1,] 1 11
[2,] 2 12
[3,] 3 13
[4,] 4 14
[5,] 5 15
[[3]]
b c
[1,] 6 11
[2,] 7 12
[3,] 8 13
[4,] 9 14
[5,] 10 15
The following sort of works but I am wondering if there is a better way?
combn(x = c("a","b","c"), m = 2, function(x) sapply(x, function(y) eval(parse(text = y))))
Here's one way (thanks to OP and #thelatemail for fixes):
M = cbind(a, b, c)
combn(seq_len(ncol(M)), 2, FUN = function(x) M[,x], simplify = FALSE)
[[1]]
a b
[1,] 1 6
[2,] 2 7
[3,] 3 8
[4,] 4 9
[5,] 5 10
[[2]]
a c
[1,] 1 11
[2,] 2 12
[3,] 3 13
[4,] 4 14
[5,] 5 15
[[3]]
b c
[1,] 6 11
[2,] 7 12
[3,] 8 13
[4,] 9 14
[5,] 10 15

What is the best way to tidy a matrix in R

Is there a best practice means of "tidying" a matrix/array? By "tidy" in this context I mean
one row per element of the matrix
one column per dimension. the elements of these columns give you the "coordinates" of the matrix element which is stored on that row
I have an example here for a 2d matrix, but ideally this would work with an array also (This example works for mm <- array(1:18, c(3,3,3)), but I thought that would be too much to paste in here)
mm <- matrix(1:9, nrow = 3)
mm
#> [,1] [,2] [,3]
#> [1,] 1 4 7
#> [2,] 2 5 8
#> [3,] 3 6 9
inds <- which(mm > -Inf, arr.ind = TRUE)
cbind(inds, value = mm[inds])
#> row col value
#> [1,] 1 1 1
#> [2,] 2 1 2
#> [3,] 3 1 3
#> [4,] 1 2 4
#> [5,] 2 2 5
#> [6,] 3 2 6
#> [7,] 1 3 7
#> [8,] 2 3 8
#> [9,] 3 3 9
as.data.frame.table One way to convert from wide to long is the following. See ?as.data.frame.table for more information. No packages are used.
mm <- matrix(1:9, 3)
long <- as.data.frame.table(mm)
The code gives this data.frame:
> long
Var1 Var2 Freq
1 A A 1
2 B A 2
3 C A 3
4 A B 4
5 B B 5
6 C B 6
7 A C 7
8 B C 8
9 C C 9
numbers
If you prefer row and column numbers:
long[1:2] <- lapply(long[1:2], as.numeric)
giving:
> long
Var1 Var2 Freq
1 1 1 1
2 2 1 2
3 3 1 3
4 1 2 4
5 2 2 5
6 3 2 6
7 1 3 7
8 2 3 8
9 3 3 9
names Note that above it used A, B, C, ... because there were no row or column names. They would have been used if present. That is, had there been row and column names and dimension names the output would look like this:
mm2 <- array(1:9, c(3, 3), dimnames = list(A = c("a", "b", "c"), B = c("x", "y", "z")))
as.data.frame.table(mm2, responseName = "Val")
giving:
A B Val
1 a x 1
2 b x 2
3 c x 3
4 a y 4
5 b y 5
6 c y 6
7 a z 7
8 b z 8
9 c z 9
3d
Here is a 3d example:
as.data.frame.table(array(1:8, c(2,2,2)))
giving:
Var1 Var2 Var3 Freq
1 A A A 1
2 B A A 2
3 A B A 3
4 B B A 4
5 A A B 5
6 B A B 6
7 A B B 7
8 B B B 8
2d only For 2d one can alternately use row and col:
sapply(list(row(mm), col(mm), mm), c)
or
cbind(c(row(mm)), c(col(mm)), c(mm))
Either of these give this matrix:
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 2 1 2
[3,] 3 1 3
[4,] 1 2 4
[5,] 2 2 5
[6,] 3 2 6
[7,] 1 3 7
[8,] 2 3 8
[9,] 3 3 9
Another method is to use arrayInd together with cbind like this.
# a 3 X 3 X 2 array
mm <- array(1:18, dim=c(3,3,2))
Similar to your code, but with the more natural arrayInd function, we have
# get array in desired format
myMat <- cbind(c(mm), arrayInd(seq_along(mm), .dim=dim(mm)))
# add column names
colnames(myMat) <- c("values", letters[24:26])
which returns
myMat
values x y z
[1,] 1 1 1 1
[2,] 2 2 1 1
[3,] 3 3 1 1
[4,] 4 1 2 1
[5,] 5 2 2 1
[6,] 6 3 2 1
[7,] 7 1 3 1
[8,] 8 2 3 1
[9,] 9 3 3 1
[10,] 10 1 1 2
[11,] 11 2 1 2
[12,] 12 3 1 2
[13,] 13 1 2 2
[14,] 14 2 2 2
[15,] 15 3 2 2
[16,] 16 1 3 2
[17,] 17 2 3 2
[18,] 18 3 3 2

Creating matrix from random block rows of another matrix without loop (in R)?

I am trying to create a matrix by drawing random block rows from another matrix. I have managed to do so with a loop.
set.seed(1)
a_matrix <- matrix(1:10,10,5) # the matrix with original sample
b_matrix <- matrix(NA,10, 5) # a matrix to store the bootstrap sample
S2<- seq(from =1 , to = 10, by =2) #[1] 1 3 5 7 9
m <- 2 # block size of m
for (r in S2){ start_point<-sample(1:(nrow(a_matrix)-1), 1, replace=T)
#randomly choose a number 1 to length of a_matrix -1
b_block <- a_matrix[start_point:(start_point+(m-1)), 1:ncol(a_matrix)]
# randomly select blocks from matrix a
b_matrix[r,]<-as.matrix((b_block)[1,])
b_matrix[(r+1),]<-as.matrix((b_block)[2,]) # put the blocks into matrix b
}
b_matrix
#we now have a b_matrix that is made of random blocks (size m=2)
#of the original a_matrix
The loop method works but it is clearly not very efficient and it is not possible to extend it to other block size (for e.g. having a blocksize of 3) .What is a cleaner and expandable approach ? Thanks in advance
Here I tried to clean it up a bit and generalize the use of m:
random_block_sample <- function(a_matrix, m = 2L) {
N <- nrow(a_matrix)
stopifnot(m <= N)
n <- ceiling(N / m)
s <- sample(N - m + 1L, n, TRUE) # start_point
i <- unlist(lapply(s, seq, length.out = m))
b_matrix <- a_matrix[i, , drop = FALSE]
head(b_matrix, N)
}
set.seed(1L)
random_block_sample(a_matrix, m = 2L)
# [,1] [,2] [,3] [,4] [,5]
# [1,] 3 3 3 3 3
# [2,] 4 4 4 4 4
# [3,] 4 4 4 4 4
# [4,] 5 5 5 5 5
# [5,] 6 6 6 6 6
# [6,] 7 7 7 7 7
# [7,] 9 9 9 9 9
# [8,] 10 10 10 10 10
# [9,] 2 2 2 2 2
# [10,] 3 3 3 3 3
set.seed(1L)
random_block_sample(a_matrix, m = 5L)
# [,1] [,2] [,3] [,4] [,5]
# [1,] 2 2 2 2 2
# [2,] 3 3 3 3 3
# [3,] 4 4 4 4 4
# [4,] 5 5 5 5 5
# [5,] 6 6 6 6 6
# [6,] 3 3 3 3 3
# [7,] 4 4 4 4 4
# [8,] 5 5 5 5 5
# [9,] 6 6 6 6 6
# [10,] 7 7 7 7 7

Resources