tibble(
A= c("x","x","y","y"),
B= c("y","y","y","y"),
C= c("x","y","z","y")
) %>%
mutate(
id = row_number(),
.before = "A"
) %>%
mutate(
neighs_id = list(
c("2"),
c("1,4"),
c("4"),
c("2,3")
)
) %>% View()
neighs_id
的输出是id_row
列表,当TRUE
==1
A
、B
或C
的值恰好从同一列中的该.row
中的值!=
的条件。
我想要一个代码将第二个突变替换为map
,该的结果是所有行的list
(保留:操作将rowise
!),给定一组列,具有1
列的值!=
列[.row]。
理论上,我可以设置一个id
Xid
的方阵,检查 tibble 的列sum
,以便column[id] =! column[column[.id]
,然后保留元素== 1
的所有匹配项,但我认为这应该是一种更直接的方法在这些"最小不同的行"上选择矢量化filter
, 给定列的选择器。
在基本 R 中:
cols = LETTERS[1:3]
tib$neighs_id <- lapply(seq(nrow(tib)),
function(i) which(sapply(seq(nrow(tib)),
function(x) sum(tib[x, cols] != tib[i, cols])) == 1))
#> pull(tib, neighs_id)
[[1]]
[1] 2
[[2]]
[1] 1 4
[[3]]
[1] 4
[[4]]
[1] 2 3
加快速度的一种方法是不使用tibbles,而是使用矩阵。我想这是因为 tibbles(或数据框)是列的列表,因此与使用矩阵相比,重复提取行的成本很高。
通过将字符矩阵更改为数字矩阵,可以实现另一个显着的改进,以便可以对某些操作进行矢量化。这样,Maël答案的内部sapply
可以替换为减法和矩阵列求和。
n.rep <- 1
tib <- tibble(
A=rep(c("x", "x", "y", "y"), n.rep),
B=rep(c("y", "y", "y", "y"), n.rep),
C=rep(c("x", "y", "z", "y"), n.rep)
)
cols <- LETTERS[1:3]
# change tibble to a matrix
tib.m <- as.matrix(tib[, cols])
# named vector used to translate values to their order
val.ord <- unique(c(tib.m))
val.ord <- setNames(seq_along(val.ord), val.ord)
# create numeric representation using the orders
tib.m[] <- val.ord[tib.m]
mode(tib.m) <- 'numeric'
tib$neighs_id <- apply(tib.m, 1, function(row)
which(colSums(t(tib.m) - row != 0) == 1))
当n.rep
为 1000 时,这将在大约一秒内完成(即,tib
是一个 4000 行的矩阵)。然而,恐怕将其扩展到1M可能仍然存在问题。为此,使用Rcpp
可能会有所帮助。