我有世界上最简单的命名向量,说:
vec <- seq(101, 126)
names(vec) <- letters
head(vec)
# a b c d e f
#101 102 103 104 105 106
现在我使用
purrr::imap
对其进行迭代。
library(purrr)
vec %>% head %>% imap_chr(~paste(.x, .y, sep="_"))
# a b c d e f
#"101_a" "102_b" "103_c" "104_d" "105_e" "106_f"
这种行为是完全正常的:
.x
迭代值,.y
迭代名称(如names
)(如果向量已命名),否则迭代索引(如seq_along
)。
我想知道是否有一种方法可以表现得好像输入向量没有命名一样,即有一种方法可以访问索引而不是名称。 预期输出是:
# a b c d e f
#"101_1" "102_2" "103_3" "104_4" "105_5" "106_6"
我目前正在使用
purrr::map2
作为解决方法,但我认为这个解决方案不够优雅(太冗长)。
vec %>% head %>% {map2_chr(., seq_along(.), ~paste(.x, .y, sep="_"))}
# Or, using Darren Tsai's more concise solution:
vec %>% head %>% map2_chr(seq_along(.), paste, sep="_")
# a b c d e f
#"101_1" "102_2" "103_3" "104_4" "105_5" "106_6"
提供非常简单的表示的问题是人们试图解决我的问题而不是回答我的问题。这非常好,但不是我想要的。我的问题是不连接
.x
和.y
。
假设我在一些观察之间有以下距离矩阵:
mat <- matrix(c(0, 1, 2, 1, 0, 4, 2, 4, 0), 3, 3)
nms <- letters[seq(3)]
names(nms) <- nms # Without this, I could use imap
dimnames(mat) <- list(nms, nms)
mat
# a b c
#a 0 1 2
#b 1 0 4
#c 2 4 0
现在我想知道每个观察值与其他观察值的距离的标准差。我可以尝试使用
imap
,但是 colnames(mat)
本身就是一个命名向量:
colnames(mat) %>% imap_dbl(~sd(mat[-.y,.x]))
#Error in -.y : invalid argument to unary operator
现在,我正在使用
map2
来解决这个问题。
colnames(mat) %>% map2_dbl(seq_along(.), ~sd(mat[-.y,.x]))
#[1] 0.7071068 2.1213203 1.4142136
如果您想使用
imap
,解决方法是使用 match
library(purrr)
vec %>% imap_chr(~paste(.x, match(.y, names(vec)), sep="_")) %>% head
# a b c d e f
#"101_1" "102_2" "103_3" "104_4" "105_5" "106_6"
我们还可以使用
map_chr
迭代其索引。
vec[] <- seq_along(vec) %>% map_chr(~paste(vec[.x], .x, sep = "_"))
head(vec)
# a b c d e f
#"101_1" "102_2" "103_3" "104_4" "105_5" "106_6"
我们可以将
str_c
与 imap
一起使用
library(stringr)
library(purrr)
vec %>%
imap_chr(~ str_c(.x, match(.y, names(vec)), sep="_"))
我相信这里的正确答案是
imap
(截至 2023 年 8 月)无法执行此行为。原因是imap
的写法。
> imap
function (.x, .f, ...)
{
.f <- as_mapper(.f, ...)
map2(.x, vec_index(.x), .f, ...)
}
<bytecode: 0x564de0b0eb08>
<environment: namespace:purrr>
在幕后
imap
也在做同样的map2
技巧。
当您检查
vec_index()
功能时...
> purrr:::vec_index
function (x)
{
names(x) %||% seq_along(x)
}
<bytecode: 0x564de0afaad0>
<environment: namespace:purrr>
%||%
函数可能比较陌生,来自rlang
:
> `%||%`
function (x, y)
{
if (is_null(x))
y
else x
}
<bytecode: 0x564ddd79c4a8>
<environment: namespace:rlang>
所有这些的总和是,当前实现的
imap
将尝试捕获 names(x)
,其中 x 是输入向量。如果这是NULL
(即没有名称)只有这样它才会捕获索引位置(使用seq_along(x)
)。因此,如果您的输入向量被命名,则在任何时候都不会计算索引并因此可用于输入函数.f
。
我们可以想象一种
ipmap
,它在初始化时将捕获名称和索引,并且您可以在每次迭代中访问其中一个或两个。这会增加一些开销,您当然可以编写自己的版本来执行此操作,但这有缺点。
以第二个示例为例,您当然可以尝试一些简单的事情,例如在输入向量上使用
unname
:
> colnames(mat) %>% unname() %>% imap_dbl(~sd(mat[-.y,.x]))
[1] 0.7071068 2.1213203 1.4142136
但是,您的输出向量现在也不再命名。虽然我们不太优雅,但我们可以将名称附加到矢量输出,但我不知道这是否适用于您的实际用例。
> colnames(mat) %>% unname() %>% imap_dbl(~sd(mat[-.y,.x])) %>% purrr::set_names(colnames(mat))
a b c
0.7071068 2.1213203 1.4142136