Triple exclamation marks on R
!!!
is usually used to evaluate a list of expressions.
library(dplyr)
library(rlang)
VC_preds <- c('mpg', 'cyl')
mtcars %>% select(!!!VC_preds) %>% head
# mpg Cyl
#Mazda RX4 21.0 6
#Mazda RX4 Wag 21.0 6
#Datsun 710 22.8 4
#Hornet 4 Drive 21.4 6
#Hornet Sportabout 18.7 8
#Valiant 18.1 6
If VC_preds
is a vector as in your example, !!
should work as well.
mtcars %>% select(!!VC_preds) %>% head
Help page of ?"!!!"
gives a better example to understand the difference.
vars <- syms(c("height", "mass"))
vars
#[[1]]
#height
#[[2]]
#mass
starwars %>% select(!!!vars)
# A tibble: 87 x 2
# height mass
# <int> <dbl>
# 1 172 77
# 2 167 75
# 3 96 32
# 4 202 136
# 5 150 49
# 6 178 120
# 7 165 75
# 8 97 32
# 9 183 84
#10 182 77
# … with 77 more rows
The use of the triple exclamation mark
There is no difference between !a
and !!!a
, since !!!a
is just !!(!a)
and because !a
is a boolean, !!(!a)
is just its double negation, therefore the same.
R - exclamation mark before variable, but no subsequent =, == or similar
edit: I was confused about the context.
!
is the logical-NOT operator in R.
As pointed out in the comments, R often allows users to pass arguments of different types. In this case ?HoltWinters
says
gamma: gamma parameter used for the seasonal component. If set to
‘FALSE’, an non-seasonal model is fitted.
So gamma
can be either a numeric value or a logical (FALSE
) value.
Since this !gamma
follows is.logical(gamma) && ...
, it will only be evaluated if gamma
is a logical (TRUE
/FALSE
) value. In this case, !gamma
is equivalent to gamma==FALSE
, but most programmers would shorten this to !gamma
(so that FALSE
becomes TRUE
and TRUE
becomes FALSE
).
We wouldn't want to test gamma=FALSE
without the is.logical()
test first, because someone might have specified gamma=0
, in which case R would evaluate 0==FALSE
, which according to its coercion rules is TRUE
.
This test could also have been written if (identical(gamma,FALSE))
- which would correctly evaluate both NULL and 0 as different from FALSE
.
In contrast, if gamma
were to be numeric, !gamma
would be shorthand for gamma != 0
.
According to R's rules for coercion from floating-point to logical, 0 gets converted to FALSE
and any non-zero, non-NA value gets converted to TRUE
(see this question for more detail).
Thus !gamma
is equivalent to gamma!=0
. Some old-school programmers use this for brevity; I don't think the brevity-clarity tradeoff is worth it, but that's just my opinion.
Is there a more robust rename alternative than select with triple exclamation mark?
An extension to your solution could be
library(tidyverse)
df[,setdiff(mapping, colnames(df))] <- NA
df %>% rename_all(~names(mapping))
# x y z
#1 1 m NA
#2 2 m NA
#3 3 m NA
Or another approach
map_dfc(setdiff(mapping, colnames(df)), ~df %>% mutate(!!.x := NA)) %>%
arrange(mapping) %>%
rename_all(~names(mapping))
Why use double exclamation marks in an if statement?
Short answer: No, there is no reason.
In your code, it's already a boolean
type, there is no need to convert it, and convert back again, you will always get the same result. Actually, if you have any boolean (true
or false
), when you use !!
with any of them, it will be converted back to it's initial value:
console.log(!!true); // Will be always "true"
console.log(typeof !!true); // It stills a "boolean" type
console.log(!!false); // Will be always "false"
console.log(typeof !!false); // It stills a "boolean" type
Double exclamation points?
This converts a value to a boolean and ensures a boolean type.
"foo" // Evaluates to "foo".
!"foo" // Evaluates to false.
!!"foo" // Evaluates to true.
If foo.bar
is passed through, then it may not be 0 but some other falsy value. See the following truth table:
Truth Table for javascript
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
" \t\r\n" == 0 // true
Source: Doug Crockford
Javascript also gets really weird when it comes to NaN values. And this is the only case I can think of off the top of my head where !! would behave differently to ===.
NaN === NaN //false
!!NaN === !!NaN //true
// !!NaN is false
How to write a simple for loop that will populate a new column based on values in an old column, using key-value pairs?
I would use dplyr::recode
:
df$newcol <- dplyr::recode(df$label, !!!index)
Output:
> df
label newcol
1 a word1
2 b word2
3 c word3
>
How to remove missing values in summarise_all dplyr
An alternative approach based on the coalesce()
function from tidyr
In the below code, we remove the type
variable since the OP indicated we don't need it in the output. We then group_by()
to essentially break up our data into separate data.frame
s for each ID
. The coalesce_by_column()
function we define then converts each of these into a list whose elements are each a vector of values for each gene
column.
We finally can pass this list to coalesce()
. coalesce()
takes a set of vectors and finds the first non-NA
value across the vectors for each index of the vectors. In practice, this means it can take multiple columns with only one or zero non-NA
value across all columns for each index and collapse them into a single column with as many non-NA
values as possible.
Usually we would have to pass each vector as its own object to coalesce()
but we can use the (splice operator)[https://stackoverflow.com/questions/61180201/triple-exclamation-marks-on-r] !!!
to pass each element of our list as its own vector. See the last example in ?"!!!"
for a demonstration.
library(dplyr)
library(tidyr)
# Define a function to coalesce by column
coalesce_by_column <- function(df) {
coalesce(!!! as.list(df))
}
# Remove NA rows
df %>%
select(-type) %>%
group_by(ID) %>%
summarise(across(.fns = coalesce_by_column))
#> # A tibble: 2 x 4
#> ID genes1 genes2 genes3
#> <dbl> <dbl> <dbl> <dbl>
#> 1 1 2 0 2
#> 2 2 1 1 1
Related Topics
Creating New Shape Palettes in Ggplot2 and Other R Graphics
Replace Na with Previous and Next Rows Mean in R
How to Increase Smoothness of Spheres3D in Rgl
Store Arrangegrob to Object, Does Not Create Printable Object
Plot with Ggplot in For-Loop Doesn't Work
Why Does As.Matrix Add Extra Spaces When Converting Numeric to Character
How to Find the Package Name in R for a Specific Function
Combine/Merge Columns While Avoiding Na
Data.Table := Assignments When Variable Has Same Name as a Column
Linking Intel's Math Kernel Library (Mkl) to R on Windows
Can Sparklyr Be Used with Spark Deployed on Yarn-Managed Hadoop Cluster
Dividing Each Cell in a Data Set by the Column Sum in R
Scoping of Variables in Aes(...) Inside a Function in Ggplot
How to Calculate Total Least Squares in R? (Orthogonal Regression)
Rstudio Calls Source() When Saving Script
How to Retrieve the Client's Current Time and Time Zone When Using Shiny