I'm using R and need to create code that checks if a matrix dimensions has 3x3. I am stuck on the proper code using if() to check the nrow and ncol. Any help or suggestion greatly appreciated.
localSmoother <- function(myMatrix = matrix(),
smoothingMatrix = matrix(1, nrow = 3, ncol = 3))
{
if(class(smoothingMatrix) != "matrix") stop ("smoothingMatrix must be a matrix.")
if(ncol(smoothingMatrix) != "3" & if(nrow(smoothingMatrix) != "3")
stop ("smoothingMatrix must have dimensions of 3x3")
print(myMatrix) # see what myMatrix is.
print(smoothingMatrix) # see what smoothingMatrix is.
return(matrix(, nrow = 3, ncol = 3))
}
# # TEST the CODE:
localSmoother(myMatrix = diag(x = 2, nrow = 5, ncol = 5), smoothingMatrix = matrix(2, nrow = 5, ncol = 3))
# # Error in localSmoother(myMatrix = diag(x = 2, nrow = 5, ncol = 5), smoothingMatrix = matrix(2, :
# # smoothingMatrix must be 3x3
Most immediately, you have two if in your conditional. You should just have one.
if(ncol(smoothingMatrix) != "3" & if(nrow(smoothingMatrix) != "3")
## should be
if(ncol(smoothingMatrix) != "3" & nrow(smoothingMatrix) != "3")
Next, your logic asks when the number of rows is not 3 and the number of columns is not 3. This condition works as intended (returns TRUE and follows to the stop statement in the if block) if your dimension is c(5,4), but would fail if your dimension is c(5,3).
x <- matrix(1:20,nrow=5)
dim(x)
# [1] 5 4
ncol(x) != "3" & nrow(x) != "3"
# [1] TRUE
x <- matrix(1:12,nrow=3)
dim(x)
# [1] 3 4
ncol(x) != "3" & nrow(x) != "3"
# [1] FALSE
I would instead use either of the following equivalent lines:
if(!(ncol(smoothingMatrix) == 3 && nrow(smoothingMatrix) == 3))
## is the same as
if(ncol(smoothingMatrix) != 3 || nrow(smoothingMatrix) != 3)
Note two things:
I'm using the logical operators && and || instead of the vectorized operators & and |. Try c(TRUE, FALSE) & TRUE vs. c(TRUE, FALSE) && TRUE.
I am using the numeric form 3 instead of the character "3". R will coerce the number to a character, so the equality test works here, but it could trip you up in other cases.
It may be easier to compare on the dimension of the matrix:
if(!isTRUE(all.equal(dim(smoothingMatrix),c(3L,3L))))
(isTRUE is needed because all.equal returns either TRUE or a character vector describing the differences. Observe that all.equal(1,0) does not return FALSE but instead a character vector describing the differences. Any if statements around all.equal then throw an error if equality doesn't hold.)
all.equal(1,0)
# [1] "Mean relative difference: 1"
if(all.equal(1,0)) print(TRUE)
# Error in if (all.equal(1, 0)) print(TRUE) :
# argument is not interpretable as logical
#Blue Magister's answer is great with nice explanations, go there first.
For your specific task, you might find the stopifnot function useful. From ?stopifnot
stopifnot(...) If any of the expressions in ... are not all TRUE, stop is called, producing an error message indicating the first of the elements of ... which were not true.
Your code could then be
stopifnot(is.matrix(smoothingMatrix),
ncol(smoothingMatrix) == 3,
nrow(smoothingMatrix) == 3)
The downside is that the error messages are a bit less helpful, the upside is that you don't have to write them yourself, and the code is nice and clean.
Related
What is the difference between = and ==? I have found cases where the double equal sign will allow my script to run while one equal sign produces an error message. When should I use == instead of =?
It depends on context as to what = means. == is always for testing equality.
= can be
in most cases used as a drop-in replacement for <-, the assignment operator.
> x = 10
> x
[1] 10
used as the separator for key-value pairs used to assign values to arguments in function calls.
rnorm(n = 10, mean = 5, sd = 2)
Because of 2. above, = can't be used as a drop-in replacement for <- in all situations. Consider
> rnorm(N <- 10, mean = 5, sd = 2)
[1] 4.893132 4.572640 3.801045 3.646863 4.522483 4.881694 6.710255 6.314024
[9] 2.268258 9.387091
> rnorm(N = 10, mean = 5, sd = 2)
Error in rnorm(N = 10, mean = 5, sd = 2) : unused argument (N = 10)
> N
[1] 10
Now some would consider rnorm(N <- 10, mean = 5, sd = 2) poor programming, but it is valid and you need to be aware of the differences between = and <- for assignment.
== is always used for equality testing:
> set.seed(10)
> logi <- sample(c(TRUE, FALSE), 10, replace = TRUE)
> logi
[1] FALSE TRUE TRUE FALSE TRUE TRUE TRUE TRUE FALSE TRUE
> logi == TRUE
[1] FALSE TRUE TRUE FALSE TRUE TRUE TRUE TRUE FALSE TRUE
> seq.int(1, 10) == 5L
[1] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
Do be careful with == too however, as it really means exactly equal to and on a computer where floating point operations are involved you may not get the answer you were expecting. For example, from ?'==':
> x1 <- 0.5 - 0.3
> x2 <- 0.3 - 0.1
> x1 == x2 # FALSE on most machines
[1] FALSE
> identical(all.equal(x1, x2), TRUE) # TRUE everywhere
[1] TRUE
where all.equal() tests for equality allowing for a little bit of fuzziness due to loss of precision/floating point operations.
= is basically a synonym for assignment ( <- ), but most often used when passing values into functions.
== is a test for equality
In the simplest of terms, take these two lines of code for example:
1) x = 10
2) x == 10
The first line (x = 10) means "I am commanding that x is equal to 10."
The second line (x == 10) means "I am asking the question, is x equal to 10?"
If you write "x == 10" first, it will give you an error message and tell you that x is not found.
If you write "x = 10," this will store x as 10.
After you have written "x = 10", then if you write "x == 10," it will respond "TRUE", as in "yes, x does equal 10, because you made x equal to 10." But if you write "x == 11" or "x == 12" or x == anything besides 10, then it will respond that "FALSE," as in "no, x does not equal 11 or 12 or anything besides 10, because you made x equal to 10."
(=) is a Assignment operator while (==) is a Equal to operator.
(=) is used for assigning the values from right to left while (==) is used for showing equality between values.
Example:
$test = 1;
if($test=2){
echo "Hello";
}
if($test==2){
echo "world";
}
//The result is Hello because = is assigning the value to $test and the second condition is false because it check the equality of $test to the value 2.
I hope this will help.
Can I pass a custom compare function to order that, given two items, indicates which one is ranked higher?
In my specific case I have the following list.
scores <- list(
'a' = c(1, 1, 2, 3, 4, 4),
'b' = c(1, 2, 2, 2, 3, 4),
'c' = c(1, 1, 2, 2, 3, 4),
'd' = c(1, 2, 3, 3, 3, 4)
)
If we take two vectors a and b, the index of the first element i at which a[i] > b[i] or a[i] < b[i] should determine what vector comes first. In this example, scores[['d']] > scores[['a']] because scores[['d']][2] > scores[['a']][2] (note that it doesn't matter that scores[['d']][5] < scores[['a']][5]).
Comparing two of those vectors could look something like this.
compare <- function(a, b) {
# get first element index at which vectors differ
i <- which.max(a != b)
if(a[i] > b[i])
1
else if(a[i] < b[i])
-1
else
0
}
The sorted keys of scores by using this comparison function should then be d, b, a, c.
From other solutions I've found, they mess with the data before ordering or introduce S3 classes and apply comparison attributes. With the former I fail to see how to mess with my data (maybe turn it into strings? But then what about numbers above 9?), with the latter I feel uncomfortable introducing a new class into my R package only for comparing vectors. And there doesn't seem to be a sort of comparator parameter I'd want to pass to order.
Here's an attempt. I've explained every step in the comments.
compare <- function(a, b) {
# subtract vector a from vector b
comparison <- a - b
# get the first non-zero result
restult <- comparison[comparison != 0][1]
# return 1 if result == 1 and 2 if result == -1 (0 if equal)
if(is.na(restult)) {return(0)} else if(restult == 1) {return(1)} else {return(2)}
}
compare_list <- function(list_) {
# get combinations of all possible comparison
comparisons <- combn(length(list_), 2)
# compare all possibilities
results <- apply(comparisons, 2, function(x) {
# get the "winner"
x[compare(list_[[x[1]]], list_[[x[2]]])]
})
# get frequency table (how often a vector "won" -> this is the result you want)
fr_tab <- table(results)
# vector that is last in comparison
last_vector <- which(!(1:length(list_) %in% as.numeric(names(fr_tab))))
# return the sorted results and add the last vectors name
c(as.numeric(names(sort(fr_tab, decreasing = T))), last_vector)
}
If you run the function on your example, the result is
> compare_list(scores)
[1] 4 2 1 3
I haven't dealt with the case that the two vectors are identical, you haven't explained how to deal with this.
The native R way to do this is to introduce an S3 class.
There are two things you can do with the class. You can define a method for xtfrm that converts your list entries to numbers. That could be vectorized, and conceivably could be really fast.
But you were asking for a user defined compare function. This is going to be slow because R function calls are slow, and it's a little clumsy because nobody does it. But following the instructions in the xtfrm help page, here's how to do it:
scores <- list(
'a' = c(1, 1, 2, 3, 4, 4),
'b' = c(1, 2, 2, 2, 3, 4),
'c' = c(1, 1, 2, 2, 3, 4),
'd' = c(1, 2, 3, 3, 3, 4)
)
# Add a class to the list
scores <- structure(scores, class = "lexico")
# Need to keep the class when subsetting
`[.lexico` <- function(x, i, ...) structure(unclass(x)[i], class = "lexico")
# Careful here: identical() might be too strict
`==.lexico` <- function(a, b) {identical(a, b)}
`>.lexico` <- function(a, b) {
a <- a[[1]]
b <- b[[1]]
i <- which(a != b)
length(i) > 0 && a[i[1]] > b[i[1]]
}
is.na.lexico <- function(a) FALSE
sort(scores)
#> $c
#> [1] 1 1 2 2 3 4
#>
#> $a
#> [1] 1 1 2 3 4 4
#>
#> $b
#> [1] 1 2 2 2 3 4
#>
#> $d
#> [1] 1 2 3 3 3 4
#>
#> attr(,"class")
#> [1] "lexico"
Created on 2021-11-27 by the reprex package (v2.0.1)
This is the opposite of the order you asked for, because by default sort() sorts to increasing order. If you really want d, b, a, c use sort(scores, decreasing = TRUE.
Here's another, very simple solution:
sort(sapply(scores, function(x) as.numeric(paste(x, collapse = ""))), decreasing = T)
What it does is, it takes all the the vectors, "compresses" them into a single numerical digit and then sorts those numbers in decreasing order.
Here is my vector:
x <- c("a", "b", "c")
I am going to extract only the odd elements from the vector so I write something like this:
ifelse(length(x) > 0, x[seq(from = 1, to = length(x), by = 2)], NA)
But the result returned is just only"a". However, if I check the condition and run the TRUE statement separately, I got different results.
length(x) > 0 #TRUE
x[seq(from = 1, to = length(x), by = 2)] # "a" "c"
Does anyone know why? Thank you!
According to ?ifelse
A vector of the same length and attributes (including dimensions and "class") as test and data values from the values of yes or no
where 'test', 'yes', 'no' are arguments
ifelse(test, yes, no)
In the concerned example, there is mismatch in length between 'test', 'yes', 'no'. The first one returns a logical vector of length 1, second of 2 and third of 1 again. This creates an imbalance. In this example, it is a TRUE, so it is returning the first element of 'yes'. Instead, we can use if/else
if(length(x) > 0) x[seq(from = 1, to = length(x), by = 2)] else NA
#[1] "a" "c"
In R, you can define an arbitrary integer sequence using :, e.g.
a = c(1:3, 12:14)
print(a)
## 1 2 3 12 13 14
I'm looking for a way to do the inverse operation, e.g. given a vector of integers I want to produce a character (or character vector) that collapses the integer sequence(s) to the equivalent expressions using :, e.g.
some_function (a)
## "1:3" "12:14"
Bonus if the stride can be detected, e.g. c(1, 3, 5) becomes "1:2:5" or something like that.
Motivation: generate an integer sequence in R based on some data manipulation to identify database row selection, and pass the most concise representation of that sequence to an external program in the proper format.
We can be able to take into consideration the rle of the differences and paste the range together taking into consideration the sequence distance.
fun=function(s){
m=c(0,diff(s))
b=rle(m)
b$values[b$lengths==1&b$values!=1]=0
l=cumsum(!inverse.rle(b))
d=function(x)paste0(range(x[,1]),
collapse = paste0(":",unique(x[-1,-1]),":"))
f=c(by(cbind(s,m),l,d))
sub("::.*","",sub(":1:",":",f))
}
fun(c(1,1:3,12:14,c(1,3,5)))
1 2 3 4
"1" "1:3" "12:14" "1:2:5"
fun(c(1, 3, 5, 8:10, 14, 17, 20))
1 2 3
"1:2:5" "8:10" "14:3:20"
fun(1)
1
"1"
Ah, nerd heaven. Here's a first shot. You could even use this for encoding within R.
Needs testing; code always prints the stride out.
encode_ranges <- function (x) {
rle_diff <- list(
start = x[1],
rled = rle(diff(x))
)
class(rle_diff) <- "rle_diff"
rle_diff
}
decode_ranges <- function (x) {
stopifnot(inherits(x, "rle_diff"))
cumsum(c(x$start, inverse.rle(x$rled)))
}
format.rle_diff <- function (x, ...) {
stopifnot(inherits(x, "rle_diff"))
output <- character(length(x$rled$values))
start <- x$start
for (j in seq_along(x$rled$values)) {
stride <- x$rled$values[j]
len <- x$rled$lengths[j]
if (len == 1L) {
start <- end + stride
next
}
end <- start + stride * x$rled$lengths[j]
output[j] <- paste(start, end, stride, sep = ":")
}
output <- output[nchar(output) > 0]
paste(output, collapse = ", ")
}
print.rle_diff <- function (x, ...) cat(format(x, ...))
encode_ranges(c(1:3, 12:14))
encode_ranges(c(1, 3, 5, 8:10, 14, 17, 20))
We create a grouping variable with diff and cumsum, then use on the group by functions to paste the range of values
f1 <- function(vec) {
unname(tapply(vec, cumsum(c(TRUE, diff(vec) != 1)),
FUN = function(x) paste(range(x), collapse=":")))
}
f1(a)
#[1] "1:3" "12:14"
For the second case
b <- c(1, 3, 5)
un1 <- unique(diff(c(1, 3, 5)))
paste(b[1], un1, b[length(b)], sep=":")
#[1] "1:2:5"
What is the difference between = and ==? I have found cases where the double equal sign will allow my script to run while one equal sign produces an error message. When should I use == instead of =?
It depends on context as to what = means. == is always for testing equality.
= can be
in most cases used as a drop-in replacement for <-, the assignment operator.
> x = 10
> x
[1] 10
used as the separator for key-value pairs used to assign values to arguments in function calls.
rnorm(n = 10, mean = 5, sd = 2)
Because of 2. above, = can't be used as a drop-in replacement for <- in all situations. Consider
> rnorm(N <- 10, mean = 5, sd = 2)
[1] 4.893132 4.572640 3.801045 3.646863 4.522483 4.881694 6.710255 6.314024
[9] 2.268258 9.387091
> rnorm(N = 10, mean = 5, sd = 2)
Error in rnorm(N = 10, mean = 5, sd = 2) : unused argument (N = 10)
> N
[1] 10
Now some would consider rnorm(N <- 10, mean = 5, sd = 2) poor programming, but it is valid and you need to be aware of the differences between = and <- for assignment.
== is always used for equality testing:
> set.seed(10)
> logi <- sample(c(TRUE, FALSE), 10, replace = TRUE)
> logi
[1] FALSE TRUE TRUE FALSE TRUE TRUE TRUE TRUE FALSE TRUE
> logi == TRUE
[1] FALSE TRUE TRUE FALSE TRUE TRUE TRUE TRUE FALSE TRUE
> seq.int(1, 10) == 5L
[1] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
Do be careful with == too however, as it really means exactly equal to and on a computer where floating point operations are involved you may not get the answer you were expecting. For example, from ?'==':
> x1 <- 0.5 - 0.3
> x2 <- 0.3 - 0.1
> x1 == x2 # FALSE on most machines
[1] FALSE
> identical(all.equal(x1, x2), TRUE) # TRUE everywhere
[1] TRUE
where all.equal() tests for equality allowing for a little bit of fuzziness due to loss of precision/floating point operations.
= is basically a synonym for assignment ( <- ), but most often used when passing values into functions.
== is a test for equality
In the simplest of terms, take these two lines of code for example:
1) x = 10
2) x == 10
The first line (x = 10) means "I am commanding that x is equal to 10."
The second line (x == 10) means "I am asking the question, is x equal to 10?"
If you write "x == 10" first, it will give you an error message and tell you that x is not found.
If you write "x = 10," this will store x as 10.
After you have written "x = 10", then if you write "x == 10," it will respond "TRUE", as in "yes, x does equal 10, because you made x equal to 10." But if you write "x == 11" or "x == 12" or x == anything besides 10, then it will respond that "FALSE," as in "no, x does not equal 11 or 12 or anything besides 10, because you made x equal to 10."
(=) is a Assignment operator while (==) is a Equal to operator.
(=) is used for assigning the values from right to left while (==) is used for showing equality between values.
Example:
$test = 1;
if($test=2){
echo "Hello";
}
if($test==2){
echo "world";
}
//The result is Hello because = is assigning the value to $test and the second condition is false because it check the equality of $test to the value 2.
I hope this will help.