changing each vector in a list - r

I'm having a fundamental problem with how the lapply function works. I want to classify each member of each vector in a list.
My list:
s <- list(
a = c(1, 20, 300),
b = c(1.1, 20.1, 300.1),
c = c(1.2, 20.2, 300.3)
)
My classification function:
classify <- function(n, peaks){
which(abs(peaks-n)==min(abs(peaks-n)))
}
My peaks:
peaks <- c(1.27350, 20.32662, 300.02650)
If I classify s$c by itself, I get the result I expect:
> sapply(s$c,classify,peaks)
[1] 1 2 3
But when I try to classify all the vectors at once, I get this:
> lapply(s,classify,peaks)
$a
[1] 3 //should be 1,2,3
$b
[1] 3 //should be 1,2,3
$c
[1] 1 //should be 1,2,3
Why am I getting the result that I do? And how do I get the result that I want?

how bout
> lapply(s,sapply,classify,peaks)
$a
[1] 1 2 3
$b
[1] 1 2 3
$c
[1] 1 2 3

First, a style point: usewhich.min for finding the location of a minimum.
classify <- function(n, peaks){
which.min(abs(peaks-n))
}
Second, break your code down a bit do see what is happening.
abs(peaks - s$a) #3rd value is smallest
abs(peaks - s$b) #3rd value is smallest
abs(peaks - s$c) #1st value is smallest
These indicies are what gets returned from the call to lapply.
Based on your comment, I guess your problem is that lapply acts on each element of a vector, when you really want to call just call it once on everything, since classify is already vectorised. Try this:
if(is.list(s)) lapply(s, classify, peaks = peaks) else classify(s, peaks)

lapply(s, function(x) classify(x, peaks)) will pass each element of the list s as n in the function classify. lapply(s, classify, peaks) passes peaks as n to classify.

Related

How do you get a pre-defined size/length of a variable in R? [duplicate]

I want to find the function in R which does the same as the function size in Matlab.
In Matlab, if A = [ 1 2 3 4 5], then size(A) = 1 5.
If A =[ 1 2 3;4 5 6], then size(A) = 3 3.
In R, I found that the function dim gives the size of a matrix, but it doesn't apply to vectors.
Please help me solve this problem.
Thanks a lot.
Try dim(A) it's equal to Matlab size(A) function
As you noted dim doesn't work on vectors. You can use this function which will take any number of vectors matrices, data.frames or lists and find their dimension or length:
DIM <- function( ... ){
args <- list(...)
lapply( args , function(x) { if( is.null( dim(x) ) )
return( length(x) )
dim(x) } )
}
# length 10 vector
a <- 1:10
# 3x3 matrix
b <- matrix(1:9,3,3)
# length 2 list
c <- list( 1:2 , 1:100 )
# 1 row, 2 column data.frame
d <- data.frame( a =1 , b = 2 )
DIM(a,b,c,d)
#[[1]]
#[1] 10
#[[2]]
#[1] 3 3
#[[3]]
#[1] 2
#[[4]]
#[1] 1 2
Vectors are dimensionless in R, they have length.
If one wants to consider a vector as a dimensioned object (and later work on that vector), s/he must use t() (transpose) (that in essence makes it 1-dimensional array).
dim(1:10) # NULL
length(1:10) # 10
dim(t(1:10)) # 1x10
Belated answer, but note that NROW and NCOL give the dimensions of both vectors and matrices/data.frames. So, for example:
> a<-c(1,2,3,4)
> NROW(a);NCOL(a)
[1] 4
[1] 1
If you don't know if the class of variable A is 'matrix' or not, then try:
if (class(A) == "matrix" | class(A) == "data.frame") {
size <- dim(A)
} else {
size <- length(A)
}
size
This should work for your case.
You can use the following command:
c(NROW(w), NCOL(w))

Can someone explain to me the problem with my sum in R

for a given matrix F I want to calculate the sum of the 2-norm of its rows, so I use the function sum() but it doesn't work as I expect it to do here an example
# The matrix F
> F <- matrix(c(9,1,1,1,4,1),nrow=3)
# index of the sum i
> i=1:NROW(F)
#And here is the result
> sum(norm(F[i,], type = "2")^4)
[1] 7376.60160040254
# and if i calculate each element of the sum i get
> norm(F[1,], type = "2")^4
[1] 6724
> norm(F[2,], type = "2")^4
[1] 289
> norm(F[3,], type = "2")^4
[1] 4
I think you're looking for the apply function. It applies a function along the dimensions of a matrix.
sum(apply(F,MARGIN = 1,function(x){norm(x,type = "2")^4}))
#[1] 7017
The reason yours doesn't work is because you assigned c(1,2,3) to i. Then, when you subset F, you just get the whole matrix.
i=1:NROW(F)
i
#[1] 1 2 3
norm(F,type="2")^4
#[1] 7376.602
norm(F[1:3,],type="2")^4
#[1] 7376.602
norm(F[i,],type="2")^4
#[1] 7376.602
Disclaimer: I have not assessed the mathematical validity of this approach, only programmatically recreated the OP's desired behavior.

function to subset data supplying subset argument as text string

m <- matrix(1:4, ncol=2)
l <- list(a=1:3, b='c')
d <- data.frame(a=1:3, b=3:1)
I was wondering if it is possible to make a function that takes a base R object (matrix, vector, list or data.frame, ...) as well as a text that specifies the subset of the object.
f1 <- function(object, subset) {
# object'subset'
}
For instance
f1(m, '[1,1]') #to evaluate m[1,1]
f1(l, '[[1]][2:3]') #l[[1]][2:3]
f1(d, '$a') #d$a
would give us (respectively):
[1] 1
[1] 2 3
[1] 1 2 3
I guess the function need somehow to glue the two arguments before evaluating. I guess one could make a kind of interpreter for each bit of the subset text and the (for the matrix example) do something like:
`[`(1,1)
This would possible but I thought there would be an easier more direct way (my 'glue' above).
Well one way to go is to use eval(parse)) methodology, i.e.
f1 <- function(x, text){
eval(parse(text = paste0(x, text)))
}
f1('d', '$a')
#[1] 1 2 3
f1('m', '[1,1]')
#[1] 1
f1('l', '[[1]][2:3]')
#[1] 2 3
f1<-function(object, subset){
return(eval(parse(text=paste0(substitute(object),subset))))
}
> m=matrix(4,2,2)
> l=list(c(1,2,3),c(2,3,4))
> f1(m,'[1,1]')
[1] 4
> f1(l,'[[1]][1:2]')
[1] 1 2

How to know a dimension of matrix or vector in R?

I want to find the function in R which does the same as the function size in Matlab.
In Matlab, if A = [ 1 2 3 4 5], then size(A) = 1 5.
If A =[ 1 2 3;4 5 6], then size(A) = 3 3.
In R, I found that the function dim gives the size of a matrix, but it doesn't apply to vectors.
Please help me solve this problem.
Thanks a lot.
Try dim(A) it's equal to Matlab size(A) function
As you noted dim doesn't work on vectors. You can use this function which will take any number of vectors matrices, data.frames or lists and find their dimension or length:
DIM <- function( ... ){
args <- list(...)
lapply( args , function(x) { if( is.null( dim(x) ) )
return( length(x) )
dim(x) } )
}
# length 10 vector
a <- 1:10
# 3x3 matrix
b <- matrix(1:9,3,3)
# length 2 list
c <- list( 1:2 , 1:100 )
# 1 row, 2 column data.frame
d <- data.frame( a =1 , b = 2 )
DIM(a,b,c,d)
#[[1]]
#[1] 10
#[[2]]
#[1] 3 3
#[[3]]
#[1] 2
#[[4]]
#[1] 1 2
Vectors are dimensionless in R, they have length.
If one wants to consider a vector as a dimensioned object (and later work on that vector), s/he must use t() (transpose) (that in essence makes it 1-dimensional array).
dim(1:10) # NULL
length(1:10) # 10
dim(t(1:10)) # 1x10
Belated answer, but note that NROW and NCOL give the dimensions of both vectors and matrices/data.frames. So, for example:
> a<-c(1,2,3,4)
> NROW(a);NCOL(a)
[1] 4
[1] 1
If you don't know if the class of variable A is 'matrix' or not, then try:
if (class(A) == "matrix" | class(A) == "data.frame") {
size <- dim(A)
} else {
size <- length(A)
}
size
This should work for your case.
You can use the following command:
c(NROW(w), NCOL(w))

fill up a matrix one random cell at a time

I am filling a 10x10 martix (mat) randomly until sum(mat) == 100
I wrote the following.... (i = 2 for another reason not specified here but i kept it at 2 to be consistent with my actual code)
mat <- matrix(rep(0, 100), nrow = 10)
mat[1,] <- c(0,0,0,0,0,0,0,0,0,1)
mat[2,] <- c(0,0,0,0,0,0,0,0,1,0)
mat[3,] <- c(0,0,0,0,0,0,0,1,0,0)
mat[4,] <- c(0,0,0,0,0,0,1,0,0,0)
mat[5,] <- c(0,0,0,0,0,1,0,0,0,0)
mat[6,] <- c(0,0,0,0,1,0,0,0,0,0)
mat[7,] <- c(0,0,0,1,0,0,0,0,0,0)
mat[8,] <- c(0,0,1,0,0,0,0,0,0,0)
mat[9,] <- c(0,1,0,0,0,0,0,0,0,0)
mat[10,] <- c(1,0,0,0,0,0,0,0,0,0)
i <- 2
set.seed(129)
while( sum(mat) < 100 ) {
# pick random cell
rnum <- sample( which(mat < 1), 1 )
mat[rnum] <- 1
##
print(paste0("i =", i))
print(paste0("rnum =", rnum))
print(sum(mat))
i = i + 1
}
For some reason when sum(mat) == 99 there are several steps extra...I would assume that once i = 91 the while would stop but it continues past this. Can somone explain what I have done wrong...
If I change the while condition to
while( sum(mat) < 100 & length(which(mat < 1)) > 0 )
the issue remains..
Your problem is equivalent to randomly ordering the indices of a matrix that are equal to 0. You can do this in one line with sample(which(mat < 1)). I suppose if you wanted to get exactly the same sort of output, you might try something like:
set.seed(144)
idx <- sample(which(mat < 1))
for (i in seq_along(idx)) {
print(paste0("i =", i))
print(paste0("rnum =", idx[i]))
print(sum(mat)+i)
}
# [1] "i =1"
# [1] "rnum =5"
# [1] 11
# [1] "i =2"
# [1] "rnum =70"
# [1] 12
# ...
See ?sample
Arguments:
x: Either a vector of one or more elements from which to choose,
or a positive integer. See ‘Details.’
...
If ‘x’ has length 1, is numeric (in the sense of ‘is.numeric’) and
‘x >= 1’, sampling _via_ ‘sample’ takes place from ‘1:x’. _Note_
that this convenience feature may lead to undesired behaviour when
‘x’ is of varying length in calls such as ‘sample(x)’. See the
examples.
In other words, if x in sample(x) is of length 1, sample returns a random number from 1:x. This happens towards the end of your loop, where there is just one 0 left in your matrix and one index is returned by which(mat < 1).
The iteration repeats on level 99 because sample() behaves very differently when the first parameter is a vector of length 1 and when it is greater than 1. When it is length 1, it assumes you a random number from 1 to that number. When it has length >1, then you get a random number from that vector.
Compare
sample(c(99,100),1)
and
sample(c(100),1)
Of course, this is an inefficient way of filling your matrix. As #josilber pointed out, a single call to sample could do everything you need.
The issue comes from how sample and which do the sampling when you have only a single '0' value left.
For example, do this:
mat <- matrix(rep(1, 100), nrow = 10)
Now you have a matrix of all 1's. Now lets make two numbers 0:
mat[15]<-0
mat[18]<-0
and then sample
sample(which(mat<1))
[1] 18 15
by adding a size=1 argument you get one or the other
now lets try this:
mat[18]<-1
sample(which(mat<1))
[1] 3 13 8 2 4 14 11 9 10 5 15 7 1 12 6
Oops, you did not get [1] 15 . Instead what happens in only a single integer (15 in this case) is passed tosample. When you do sample(x) and x is an integer, it gives you a sample from 1:x with the integers in random order.

Resources