ティブル内のソースベクトルと比較ベクトルの間のジャッカード類似度を計算しようとしています。
まず、names_フィールド(文字列のベクトル)を使用してtibbleを作成します。 dplyrの変異を使用して、names_vec(リスト列)を作成します。ここで、各行はベクトルになります(ベクトルの各要素は文字になります)。
次に、Jaccardの類似性を計算することになっている列jaccard_simを使用して新しいtibbleを作成します。
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のすべての値はゼロです。ただし、このようなものを実行すると、最初のエントリに対して0.2の正しいJaccard類似性が得られます。
a <- length(intersect(source_vec, df_comp[[1,2]]))
b <- length(union(source_vec, df_comp[[1,2]]))
a/b
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つのリスト列と他の1つのベクトルを指定しました。これらの2つのオブジェクトには、交差がありません。
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