What do ..1 and ..2 stand for in R? [duplicate] - r

This question already has answers here:
Two dots, ".." in R
(3 answers)
Closed 8 years ago.
Quote from R language definition:
Notice that identifiers starting with a period are not by default
listed by the ls function and that ‘...’ and ‘..1’, ‘..2’, etc. are
special.
The following identifiers have a special meaning and cannot be used
for object names if else repeat while function for in next break TRUE
FALSE NULL Inf NaN NA NA_integer_ NA_real_ NA_complex_ NA_character_
... ..1 ..2 etc.
However it does not give any further detail. Could anyone elaborate?

These are used to positionally extract values from the ... argument of a function. See example below:
myfun <- function(...) {
list(a = ..1, b = ..2, c = ..3)
}
myfun(1,2,3)
# $a
# [1] 1
# $b
# [1] 2
# $c
# [1] 3
myfun(3,2,1)
# $a
# [1] 3
# $b
# [1] 2
# $c
# [1] 1
myfun(1:5, "hello", letters[1:3])
# $a
# [1] 1 2 3 4 5
# $b
# [1] "hello"
# $c
# [1] "a" "b" "c"
This use becomes obvious if you try to call one of these from the console:
> ..1
Error: ..1 used in an incorrect context, no ... to look in

Related

is there a way I can recycle elements of the shorter list in purrr:: map2 or purrr::walk2?

purrr does not seem to support recycling of elements of a vector in case there is a shortage of elements in one of the two (while using purrr::map2 or purrr::walk2). Unlike baseR where we just get a warning if the larger vector is not a multiple of the shorter one.
Consider this toy example:
This works:
map2(1:3,4:6,sum)
#
#[[1]]
#[1] 5
#[[2]]
#[1] 7
#[[3]]
#[1] 9
And this doesn't work:
map2(1:3,4:9,sum)
Error: .x (3) and .y (6) are different lengths
I understand very well why this is not allowed - as it can make catching bugs very difficult. But is there any way in purrr I can force this to happen? Perhaps using some base R trick with purrr?
You can put both lists in a data frame and let that command repeat your vectors:
input <- data.frame(a = 1:3, b = 4:9)
purrr::map2(input$a, input$b, sum)
It's by design with purrr but you can use Map :
Map(sum,1:3,4:9)
# [[1]]
# [1] 5
#
# [[2]]
# [1] 7
#
# [[3]]
# [1] 9
#
# [[4]]
# [1] 8
#
# [[5]]
# [1] 10
#
# [[6]]
# [1] 12
And here's how I would recycle if I had to :
x <- 1:3
y <- 4:9
l <- max(length(y), length(x))
map2(rep(x,len = l), rep(y,len = l),sum)
# [[1]]
# [1] 5
#
# [[2]]
# [1] 7
#
# [[3]]
# [1] 9
#
# [[4]]
# [1] 8
#
# [[5]]
# [1] 10
#
# [[6]]
# [1] 12

Make elements NA depending on a predicate function

How can I easily change elements of a list or vectors to NAs depending on a predicate ?
I need it to be done in a single call for smooth integration in dplyr::mutate calls etc...
expected output:
make_na(1:10,`>`,5)
# [1] 1 2 3 4 5 NA NA NA NA NA
my_list <- list(1,"a",NULL,character(0))
make_na(my_list, is.null)
# [[1]]
# [1] 1
#
# [[2]]
# [1] "a"
#
# [[3]]
# [1] NA
#
# [[4]]
# character(0)
Note:
I answered my question as I have one solution figured out but Id be happy to get alternate solutions. Also maybe this functionality is already there in base R or packaged in a prominent library
Was inspired by my frustration in my answer to this post
We can build the following function:
make_na <- function(.x,.predicate,...) {
is.na(.x) <- sapply(.x,.predicate,...)
.x
}
Or a bit better to leverage purrr's magic :
make_na <- function(.x,.predicate,...) {
if (requireNamespace("purrr", quietly = TRUE)) {
is.na(.x) <- purrr::map_lgl(.x,.predicate,...)
} else {
if("formula" %in% class(.predicate))
stop("Formulas aren't supported unless package 'purrr' is installed")
is.na(.x) <- sapply(.x,.predicate,...)
}
.x
}
This way we'll be using purrr::map_lgl if library purrr is available, sapply otherwise.
Some examples :
make_na <- function(.x,.predicate,...) {
is.na(.x) <- purrr::map_lgl(.x,.predicate,...)
.x
}
Some use cases:
make_na(1:10,`>`,5)
# [1] 1 2 3 4 5 NA NA NA NA NA
my_list <- list(1,"a",NULL,character(0))
make_na(my_list, is.null)
# [[1]]
# [1] 1
#
# [[2]]
# [1] "a"
#
# [[3]]
# [1] NA
#
# [[4]]
# character(0)
make_na(my_list, function(x) length(x)==0)
# [[1]]
# [1] 1
#
# [[2]]
# [1] "a"
#
# [[3]]
# [1] NA
#
# [[4]]
# [1] NA
If purrr is installed we can use this short form:
make_na(my_list, ~length(.x)==0)

How to Display or Print Contents of Environment in R

I hope this question is not a duplicate, because I searched it didn't find any answer(If its a dupe, please let me know I shall remove it).
I am trying to print/display the contents of an environment, but I am unable to do it.
library(rlang)
e1 <- env(a = 1:10, b= letters[1:5])
When I use print, It just give me memory address not the contents(names and values) of that environment.
> print(e1)
<environment: 0x00000000211fbae8>
Note: I can see the env. contents in R studio Environments tab, I am using R version: "R version 3.4.2" and rlang: rlang_0.2.0
My question is : What is the right function to print contents of an environment, Sorry the question may be naive, but I am unable to figure out.
Thanks in advance
We can use get with envir parameter to get values out of specific environment
sapply(ls(e1), function(x) get(x, envir = e1))
#$a
# [1] 1 2 3 4 5 6 7 8 9 10
#$b
#[1] "a" "b" "c" "d" "e"
where
ls(e1) # gives
#[1] "a" "b"
We can use mget
mget(ls(e1), envir = e1)
#$a
#[1] 1 2 3 4 5 6 7 8 9 10
#$b
#[1] "a" "b" "c" "d" "e"
An option can be as:
lapply(ls(),function(x)get(x))
which prints content of the global environment.
#Result:
# [[1]]
# [1] 1 2
#
# [[2]]
# [1] 1 4
#
# [[3]]
# [1] 1 1
#
# [[4]]
# function (snlq)
# {
# j <- 1
# for (i in 1:length(snlq)) {
# ind <- index(snlq[[i]])
# if (identical(ind[length(ind)], "2018-05-04") == FALSE) {
# ss[j] <- i
# j <- j + 1
# }
# }
# return(ss)
# }
# <bytecode: 0x000000001fa07290>
#
#... so on

replace list element with another list element based on name

I have two list m.list and the other r.list. The m.list has NA values. For those that have NA values, I would like to replace it with elements from r.list. The problem is when I use replace function in R it is taking the index of the r.list and returns a incorrect value. Below is reproducible examples. IS there a way to replace value of one list based on the element name from another list?
m.list <- list(a= 1,b=NA,c=3,d=NA)
r.list <- list(a= 4,d=8,c=9)
mr.list <- replace(m.list, which(is.na(m.list)), r.list[which(is.na(m.list))])
Here is the output that I get b should be NA and d should be 8:
> mr.list
$a
[1] 1
$b
[1] 8
$c
[1] 3
$d
NULL
here is the desired output:
$a
[1] 1
$b
[1] NA
$c
[1] 3
$d
[1] 8
Here is an option with modifyList
modifyList(m.list, r.list[intersect(names(r.list), names(which(is.na(m.list))))])
#$a
#[1] 1
#$b
#[1] NA
#$c
#[1] 3
#$d
#[1] 8
If we split the code, the idea is to find the names in 'm.list' where the value is missing and that is also found in 'r.list' (intersect), then with modifyList replace the values in 'm.list' with the subset of 'r.list'
nm1 <- intersect(names(r.list), names(which(is.na(m.list))))
modifyList(m.list, r.list[nm1])
You can use replace but you need to match the names of the lists and make sure that you also include the is.na(m.list) in the replacement argument to ensure equal lengths
l1 <- replace(m.list, is.na(m.list),
r.list[match(names(m.list),
names(r.list))][is.na(m.list)])
which gives,
$a
[1] 1
$b
NULL
$c
[1] 3
$d
[1] 8
NOTE: To replace NULL with NA, simply,
l1[lengths(l1) == 0] <- NA
We can first find the position of the value that is to be replaced. It must exist in r.list
a=names(m.list)%in%names(r.list)&is.na(m.list)
replace(m.list, a, r.list[names(which(a))])
$a
[1] 1
$b
[1] NA
$c
[1] 3
$d
[1] 8

R list from variables using variable names [duplicate]

This question already has answers here:
Can lists be created that name themselves based on input object names?
(4 answers)
Closed 4 years ago.
I've looked extensively for a solution for this very simple task and though I have a solution, it seems like there must be a better way. The task is to create a list from a set of variables, using the variable names as names for each element in the list, e.g.:
a <- 2
b <- 'foo'
c <- 1:4
My current solution:
named.list <- function(...) {
l <- list(...)
names(l) <- sapply(substitute(list(...)), deparse)[-1]
l
}
named.list(a,b,c)
Produces:
$a
[1] 2
$b
[1] "foo"
$c
[1] 1 2 3 4
A couple of ways I can think of include mget (make assumptions about the environment your objects are located in):
mget( c("a","b","c") )
$a
[1] 2
$b
[1] "foo"
$c
[1] 1 2 3 4
Or as an edit to your function, you could use, match.call like this:
named.list <- function(...) {
l <- list(...)
names(l) <- as.character( match.call()[-1] )
l
}
named.list( a,b,c)
$a
[1] 2
$b
[1] "foo"
$c
[1] 1 2 3 4
Or you can do it in one go using setNames like this:
named.list <- function(...) {
l <- setNames( list(...) , as.character( match.call()[-1]) )
l
}
named.list( a,b,c)
$a
[1] 2
$b
[1] "foo"
$c
[1] 1 2 3 4
If you already using tidyverse packages, then you might be interested in using the tibble::lst function which does this
tibble::lst(a, b, c)
# $a
# [1] 2
#
# $b
# [1] "foo"
#
# $c
# [1] 1 2 3 4

Resources