Let's say we have a statement that produces integer(0), e.g.
a <- which(1:3 == 5)
What is the safest way of catching this?
That is R's way of printing a zero length vector (an integer one), so you could test for a being of length 0:
R> length(a)
[1] 0
It might be worth rethinking the strategy you are using to identify which elements you want, but without further specific details it is difficult to suggest an alternative strategy.
If it's specifically zero length integers, then you want something like
is.integer0 <- function(x)
{
is.integer(x) && length(x) == 0L
}
Check it with:
is.integer0(integer(0)) #TRUE
is.integer0(0L) #FALSE
is.integer0(numeric(0)) #FALSE
You can also use assertive for this.
library(assertive)
x <- integer(0)
assert_is_integer(x)
assert_is_empty(x)
x <- 0L
assert_is_integer(x)
assert_is_empty(x)
## Error: is_empty : x has length 1, not 0.
x <- numeric(0)
assert_is_integer(x)
assert_is_empty(x)
## Error: is_integer : x is not of class 'integer'; it has class 'numeric'.
Maybe off-topic, but R features two nice, fast and empty-aware functions for reducing logical vectors -- any and all:
if(any(x=='dolphin')) stop("Told you, no mammals!")
Inspired by Andrie's answer, you could use identical and avoid any attribute problems by using the fact that it is the empty set of that class of object and combine it with an element of that class:
attr(a, "foo") <- "bar"
identical(1L, c(a, 1L))
#> [1] TRUE
Or more generally:
is.empty <- function(x, mode = NULL){
if (is.null(mode)) mode <- class(x)
identical(vector(mode, 1), c(x, vector(class(x), 1)))
}
b <- numeric(0)
is.empty(a)
#> [1] TRUE
is.empty(a,"numeric")
#> [1] FALSE
is.empty(b)
#> [1] TRUE
is.empty(b,"integer")
#> [1] FALSE
if ( length(a <- which(1:3 == 5) ) ) print(a) else print("nothing returned for 'a'")
#[1] "nothing returned for 'a'"
On second thought I think any is more beautiful than length(.):
if ( any(a <- which(1:3 == 5) ) ) print(a) else print("nothing returned for 'a'")
if ( any(a <- 1:3 == 5 ) ) print(a) else print("nothing returned for 'a'")
You can easily catch integer(0) with function identical(x,y)
x = integer(0)
identical(x, integer(0))
[1] TRUE
foo = function(x){identical(x, integer(0))}
foo(x)
[1] TRUE
foo(0)
[1] FALSE
another option is rlang::is_empty (useful if you're working in the tidyverse)
The rlang namespace does not seem to be attached when attaching the tidyverse via library(tidyverse) - in this case you use purrr::is_empty, which is just imported from the rlang package.
By the way, rlang::is_empty uses user Gavin's approach.
rlang::is_empty(which(1:3 == 5))
#> [1] TRUE
isEmpty() is included in the S4Vectors base package. No need to load any other packages.
a <- which(1:3 == 5)
isEmpty(a)
# [1] TRUE
Related
Let's say I have this list:
List_example <- list('short'= 10,'medium'= 20,'long'=200)
How do I check can I check if short, medium and long are integers in one go?
Try the code below
> all(sapply(List_example, `%%`, 1) == 0)
[1] TRUE
With sapply :
List_example <- list('short'= 10,'medium'= 20,'long'=200)
all(sapply(List_example, is.numeric))
#[1] TRUE
To check for integers specifically use is.integer.
If an object has an R integer type then clearly it is a whole number or if it has a double type then we can check if it equals itself rounded.
is_int <- function(x) is.integer(x) || (is.numeric(x) && identical(round(x), x))
all(sapply(List_example, is_int))
## [1] TRUE
L <- list(3, 5L, "xyz")
all(sapply(L, is_int))
## [1] FALSE
If what you mean is that you want to find out if they all have R integer type then we have the following since the numbers in the example are all doubles.
all(sapply(List_example, is.integer))
## [1] FALSE
I am implementing a replacement for the subset operator in an S3 class. I followed the advice on
How to define the subset operators for a S4 class?
However I am having a special problem. How do I distinguish in R code if someone wrote x[i] or x[i,]. In both cases, the variable j just comes back missing.
setOldClass("myclass")
'[.myclass' <- function(x, i, j, ..., drop=TRUE) {
print(missing(j))
return(invisible(NULL))
}
And as a result I get:
x <- structure(list(), class="myclass")
> x[i]
[1] TRUE
> x[i,]
[1] TRUE
> x[i,j]
[1] FALSE
I don't see a way on how to distinguish between the two. I assume the internal C code does it by looking at the length of the argument pairlist, but is there a way to do the same in native R?
Thanks!
From alexis_laz's comment:
See, perhaps, how [.data.frame handles arguments and nargs()
Inside the function call nargs() to see how many arguments were supplied, including missing ones.
> myfunc = function(i, j, ...) {
+ nargs()
+ }
>
> myfunc()
[1] 0
> myfunc(, )
[1] 2
> myfunc(, , )
[1] 3
> myfunc(1)
[1] 1
> myfunc(1, )
[1] 2
> myfunc(, 1)
[1] 2
> myfunc(1, 1)
[1] 2
This should be enough to help you figure out which arguments were passed in the same fashion as [.data.frame.
Is there an easy, straightforward way (possibly a builtin function) that could match one vector as a whole in another vector?
Example:
target <- c(1,2,3)
A <- c(4,5,6,1,2,3)
B <- c(4,5,6,3,2,1)
my_match(target, A) # TRUE
my_match(target, B) # FALSE
I tried %in%, match and pmatch but these won't give the desired result. For example, both target %in% A and target %in% B will give the result [1] TRUE TRUE TRUE, which is not what I want.
Here another version
multi_match=function(target,A) {
lA=length(A)
lt=length(target)
if (lt>lA) return(FALSE)
any(colSums(sapply(1:(lA-lt+1),function(i) A[i:(i+lt-1)])==target)==lt)
}
Let's try it with some data
target <- c(1,2,3)
A <- c(4,5,6,1,2,3,1,2,3,1,3)
B <- c(4,5,6,3,2,1)
multi_match(target,A)
#TRUE
multi_match(target,B)
#FALSE
#"wrong" input order - trivially no match
multi_match(A,target)
#FALSE
And an extension of the multi_match function above to multi_which.
multi_which=function(target,A) {
lA=length(A)
lt=length(target)
if (lt>lA) return(integer(0))
which(colSums(sapply(1:(lA-lt+1),function(i) A[i:(i+lt-1)])==target)==lt)
}
multi_which(target,A)
#[1] 4 7
multi_which(target,B)
#integer(0)
#"wrong" input order - trivially no match
multi_which(A,target)
#integer(0)
Try:
grepl(paste(target,collapse=","),paste(A,collapse=","))
grepl(paste(target,collapse=","),paste(B,collapse=","))
This concatenates the vectors into strings and looks for a substring in the second argument that matches the first.
You could put this into a function that returns true or false:
my_match <- function(x,y,dlm=",") grepl(paste(x,collapse=dlm),paste(y,collapse=dlm))
my_match(target,A)
[1] TRUE
my_match(target,B)
[1] FALSE
One possible way is to use match and check if resulting sequence is rising
all(diff(match(target, A)) == 1) && length(match(target, A)) == length(target)
Or as a function
> exact_match <- function(p, x) all(diff(match(p, x)) == 1) && length(match(p, x)) == length(p)
> exact_match(target,A)
[1] TRUE
> exact_match(target,B)
[1] FALSE
I want to compare two vectors but it is not working, kindly tell me how two vectors can be compared:
x <- c(1,2,3,4)
y <- c(5,6,7,8)
if (x==y) print("same") else print("different")
Use all can work here.
> all(x==y)
[1] FALSE
> y1=c(5,6,7,8)
> all(y==y1)
[1] TRUE
EDIT
best is to use isTRUE(all.equal(x,y)) to avoid recycling
recycling
> x=c(5,6,5,6)
> y=c(5,6)
> all(x==y)
[1] TRUE
better way
> isTRUE(all.equal(x,y))
[1] FALSE
> isTRUE(all.equal(y,y1))
[1] TRUE
> x=c(5,6,5,6)
> y=c(5,6)
>isTRUE(all.equal(x,y))
[1] FALSE
When it comes to array comparison, all and any are your friends. If you do not really mean geometric vector but array of values, sort should also be necessary:
> all(sort(x)==sort(y))
Try:
x <- c(1,2,3,4)
y <- c(5,6,7,8)
if(identical(x,y)) print("identical") else print("not identical")
I am using the bit64 package in some R code. I have created a vector
of 64 bit integers and then tried to use sapply to iterate over these
integers in a vector. Here is an example:
v = c(as.integer64(1), as.integer64(2), as.integer64(3))
sapply(v, function(x){is.integer64(x)})
sapply(v, function(x){print(x)})
Both the is.integer64(x) and print(x) give the incorrect
(or at least) unexpected answers (FALSE and incorrect float values).
I can circumvent this by directly indexing the vector c but I have
two questions:
Why the type conversion? Is their some rule R uses in such a scenario?
Any way one can avoid this type conversion?
TIA.
Here is the code of lapply:
function (X, FUN, ...)
{
FUN <- match.fun(FUN)
if (!is.vector(X) || is.object(X))
X <- as.list(X)
.Internal(lapply(X, FUN))
}
Now check this:
!is.vector(v)
#TRUE
as.list(v)
#[[1]]
#[1] 4.940656e-324
#
#[[2]]
#[1] 9.881313e-324
#
#[[3]]
#[1] 1.482197e-323
From help("as.list"):
Attributes may be dropped unless the argument already is a list or
expression.
So, either you creaste a list from the beginning or you add the class attributes:
v_list <- lapply(as.list(v), function(x) {
class(x) <- "integer64"
x
})
sapply(v_list, function(x){is.integer64(x)})
#[1] TRUE TRUE TRUE
The package authours should consider writing a method for as.list. Might be worth a feature request ...