I often use all
function and, whenever I get TRUE
, I find myself checking that none of the elements of the comparison is NULL
or empty because this could give a false TRUE
result.
Example:
y<-1:10
z<-5:15
# I make a comparison which is really true
all(y[y>5 & y<10]==z[z>5 & z<10])
[1] TRUE
# Now I make a typo because I often do, but I don't notice:
all(y[y>5 & y<0]==z[z>5 & z<10])
[1] TRUE
# the result is also true but only because y[y>5 & y<0] is empty:
y[y>5 & y<0]
#integer(0)
So, in the second case, if I don't check each element of all
, I will go one with my code, thinking everything went well and, of course, the final result will be incorrect.
Instead of checking the element I put in my all
call, I could add a length
call: (all(y[y>5 & y<0]==z[z>5 & z<10]) & length(y[y>5 & y<0])>0 & length(z[z>5 & z<10]>0)
but that seems rather tedious...
Is there a way to make all
return NA
or FALSE
when either element is of length 0 (all
help is not very helpful on that subject) or is there an alternative function that would do that ?
EDIT
Thanks to @Metrics, there is an alternative with function identical
:
identical(y[y>5 & y<0],z[z>5 & z<10])
[1] FALSE
Although identical
doesn't return TRUE
in this case, it still doesn't warn me that something is going wrong...
The ideal solution would return a warning telling, for example, that one element is empty.
The documentation for all
clearly says:
That all(logical(0)) is true is a useful convention: it ensures that
all(all(x), all(y)) == all(x, y) even if x has length zero.
so there is no way to obtain your desired result with all
.
As noted in the comments, identical
and all.equal
are closer matches to your request. However, identical
wouldn't warn you if the objects under comparison are of different length. The drawback of all.equal
is that it wouldn't return you a logical value in the case of different lengths:
all.equal(y[y>5 & y<0],z[z>5 & z<10])
# [1] "Numeric: lengths (0, 4) differ"
and I believe that the official documentation suggests not to use all.equal
directly in if
expressions:
Do not use all.equal directly in if expressions—either use isTRUE(all.equal(....)) or identical if appropriate.
However, isTRUE(all.equal(y[y>5 & y<0],z[z>5 & z<10]))
wouldn't tell you about different lengths.
[Solution]
You can simply write your own function for this purpose and add some syntactic sugar for convenience:
'%=%' <- function(a,b) {
if (length(a)!=length(b)) warning('Objects are of different length')
identical(a,b)
}
It will return TRUE
if the objects are identical
y[y>5 & y<10] %=% z[z>5 & z<10]
# [1] TRUE
and FALSE
if the objects are different (+warning if they are of different length):
y[y>5 & y<0] %=% z[z>5 & z<10]
# [1] FALSE
# Warning message:
# In y[y > 5 & y < 0] %=% z[z > 5 & z < 10] :
# Objects are of different length
I don't think you should use either identical
or all.equal
for this purpose. As already outlined, the latter doesn't return always a logical vector, while the first is much more stringent and should be used only to check if two objects are really the same. Consider this example:
y<-setNames(1:10,letters[1:10])
z<-5:15
identical(y[y>5 & y<10],z[z>5 & z<10])
it gives FALSE
because y
has names. The all
function is the way to go. If you are really bothered about the zero length issue, try:
myAll <- function(x,na.rm=FALSE) {
if (length(x)==0) {
warning("zero length argument")
return(TRUE)
}
all(x,na.rm=na.rm)
}
Of course you can change the behaviour when x
has zero length or you can define a binary operator as already mentioned.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With