在dplyr中使用列表列函数进行变异

mat*_*sho 8 text r dplyr mutate

我试图计算源向量和tibble中的比较向量之间的Jaccard相似性.

首先,使用names_字段(字符串向量)创建一个tibble.使用dplyr的mutate,创建names_vec,列表列,其中每行现在是一个向量(向量的每个元素都是一个字母).

然后,使用列jaccard_sim创建一个新的tibble,它应该计算Jaccard相似度.

source_vec <- c('a', 'b', 'c')

df_comp <- tibble(names_ = c("b d f", "u k g", "m o c"),
              names_vec = strsplit(names_, ' '))

df_comp_jaccard <- df_comp %>%
   dplyr::mutate(jaccard_sim = length(intersect(names_vec, source_vec))/length(union(names_vec, source_vec)))
Run Code Online (Sandbox Code Playgroud)

jaccard_sim中的所有值都为零.但是,如果我们运行这样的东西,我们得到第一个条目的正确的Jaccard相似度为0.2:

a <- length(intersect(source_vec, df_comp[[1,2]]))
b <- length(union(source_vec, df_comp[[1,2]]))
a/b
Run Code Online (Sandbox Code Playgroud)

Moo*_*per 11

你可以简单地添加 rowwise

df_comp_jaccard <- df_comp %>%
  rowwise() %>%
  dplyr::mutate(jaccard_sim = length(intersect(names_vec, source_vec))/
                              length(union(names_vec, source_vec)))

# A tibble: 3 x 3
  names_ names_vec jaccard_sim
   <chr>    <list>       <dbl>
1  b d f <chr [3]>         0.2
2  u k g <chr [3]>         0.0
3  m o c <chr [3]>         0.2
Run Code Online (Sandbox Code Playgroud)

使用时,rowwise您可以获得最期望的直观行为mutate:"为每一行执行此操作".

不使用rowwise意味着你利用矢量化函数,这更快,这就是为什么它是默认值,但如果你不小心可能会产生意想不到的结果.

印象中mutate(或其他dplyr功能)的作品横行是一种假象,由于你与矢量函数工作的事实,其实你总是以饱满的列杂耍.

我将用几个例子来说明:

有时结果是相同的,使用矢量化函数,例如paste:

tibble(a=1:10,b=10:1) %>% mutate(X = paste(a,b,sep="_"))
tibble(a=1:10,b=10:1) %>% rowwise %>% mutate(X = paste(a,b,sep="_"))
# # A tibble: 5 x 3
#       a     b     X
#   <int> <int> <chr>
# 1     1     5   1_5
# 2     2     4   2_4
# 3     3     3   3_3
# 4     4     2   4_2
# 5     5     1   5_1
Run Code Online (Sandbox Code Playgroud)

有时它是不同的,具有未矢量化的功能,例如max:

tibble(a=1:5,b=5:1) %>% mutate(max(a,b))
# # A tibble: 5 x 3
#       a     b `max(a, b)`
#   <int> <int>       <int>
# 1     1     5           5
# 2     2     4           5
# 3     3     3           5
# 4     4     2           5
# 5     5     1           5

tibble(a=1:5,b=5:1) %>% rowwise %>% mutate(max(a,b))
# # A tibble: 5 x 3
#       a     b `max(a, b)`
#   <int> <int>       <int>
# 1     1     5           5
# 2     2     4           4
# 3     3     3           3
# 4     4     2           4
# 5     5     1           5
Run Code Online (Sandbox Code Playgroud)

请注意,在这种情况下,您不应该rowwise在现实生活中使用,但pmax为此目的进行了矢量化:

tibble(a=1:5,b=5:1) %>% mutate(pmax(a,b))
# # A tibble: 5 x 3
#       a     b `pmax(a, b)`
#   <int> <int>        <int>
# 1     1     5            5
# 2     2     4            4
# 3     3     3            3
# 4     4     2            4
# 5     5     1            5
Run Code Online (Sandbox Code Playgroud)

Intersect就是这样的函数,你给这个函数提供了一个包含向量和另一个向量的列表列,这两个对象没有交集.


akr*_*run 9

我们可以map用来循环list

library(tidyverse)
df_comp %>% 
     mutate(jaccard_sim = map_dbl(names_vec, ~length(intersect(.x, 
                 source_vec))/length(union(.x, source_vec))))
# A tibble: 3 x 3
#   names_ names_vec jaccard_sim
#    <chr>    <list>       <dbl>
#1  b d f <chr [3]>         0.2
#2  u k g <chr [3]>         0.0
#3  m o c <chr [3]>         0.2
Run Code Online (Sandbox Code Playgroud)

map功能进行了优化.以下是system.time稍大的数据集

df_comp1 <- df_comp[rep(1:nrow(df_comp), 1e5),]
system.time({

 df_comp1 %>%
      rowwise() %>%
      dplyr::mutate(jaccard_sim = length(intersect(names_vec, source_vec))/length(union(names_vec, source_vec)))
    })
 #user  system elapsed 
 # 25.59    0.05   25.96 

system.time({
  df_comp1 %>% 
     mutate(jaccard_sim = map_dbl(names_vec, ~length(intersect(.x, 
                 source_vec))/length(union(.x, source_vec))))
   })
#user  system elapsed 
#  13.22    0.00   13.22 
Run Code Online (Sandbox Code Playgroud)