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

时间:2017-10-23 09:50:37

标签: r text 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)))

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

2 个答案:

答案 0 :(得分:9)

您只需添加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

使用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

有时候它会有所不同,其功能没有矢量化,例如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

请注意,在这种情况下,您不应在现实生活中使用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

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

答案 1 :(得分:8)

我们可以使用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

优化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