在 R 中对数据框进行双重循环(使用 Levenshtein Dist 比较行)的最聪明方法?
Smartest way to double loop over a data frame (comparing rows to each other with a Levenshtein Dist) in R?
我在几条记录上制作了 paramStrings 的 df:
idName Str
1 Аэрофлот_Эконом 95111000210102121111010100111000100110101001
2 Аэрофлот_Комфорт 95111000210102121111010100111000100110101001
3 Аэрофлот_Бизнес 96111000210102121111010100111000100110101001
4 Трансаэро_Дисконт 26111000210102120000010100001010000010001000
5 Трансаэро_Туристический 26111000210002120000010100001010000010001000
6 Трансаэро_Эконом 26111000210002120000010100001010000010001000
现在我需要用一个 levenshtainDist 将每个与其他的进行比较,它作为一个函数(str1,str2)工作,所以我显然需要双循环。但是,我很确定应该有一种简洁的矢量化 (apply/lapply/sapply) 方法来做到这一点,但是我找不到任何类似的解决方案...
函数 adist
计算广义 Levenshtein 距离。这是你需要的吗?
假设您的数据在 data.frame 中,使用:adist(mydf$Str)
将 return 一个矩阵,其中每对 Str
列之间的距离。
因为你有一个 data.frame 我认为做双循环的最好方法是 lapply
/sapply
双循环,它与 data.frames
一起工作很好:
例如:
df1 <- data.frame(a=1:20,b=1:20) #example dataframe
a <- data.frame(lapply(1:nrow(df1), function(x) {
sapply(1:nrow(df1), function(y) {
sum( df1[x,2], df1[y,2]) #I just add the two cells (I only use the second column here for the demonstration) / replace with your function
}
)
}
)
)
colnames(a) <- 1:20 #change names
第一个 lapply
将 return nrow(df1)
列表,每个列表内将是一个 nrow(df1)
观察向量(函数的评估)。这意味着你将有一个 nrow(df1)
xnrow(df1)
列表,它可以很方便地转换为 data.frame
,就像我在上面所做的那样。因此你有一个 nrow(df1)
xnrow(df1)
data.frame
.
上面的输出:
> str(a)
'data.frame': 20 obs. of 20 variables:
$ 1 : int 2 3 4 5 6 7 8 9 10 11 ...
$ 2 : int 3 4 5 6 7 8 9 10 11 12 ...
$ 3 : int 4 5 6 7 8 9 10 11 12 13 ...
$ 4 : int 5 6 7 8 9 10 11 12 13 14 ...
$ 5 : int 6 7 8 9 10 11 12 13 14 15 ...
$ 6 : int 7 8 9 10 11 12 13 14 15 16 ...
$ 7 : int 8 9 10 11 12 13 14 15 16 17 ...
$ 8 : int 9 10 11 12 13 14 15 16 17 18 ...
$ 9 : int 10 11 12 13 14 15 16 17 18 19 ...
$ 10: int 11 12 13 14 15 16 17 18 19 20 ...
$ 11: int 12 13 14 15 16 17 18 19 20 21 ...
$ 12: int 13 14 15 16 17 18 19 20 21 22 ...
$ 13: int 14 15 16 17 18 19 20 21 22 23 ...
$ 14: int 15 16 17 18 19 20 21 22 23 24 ...
$ 15: int 16 17 18 19 20 21 22 23 24 25 ...
$ 16: int 17 18 19 20 21 22 23 24 25 26 ...
$ 17: int 18 19 20 21 22 23 24 25 26 27 ...
$ 18: int 19 20 21 22 23 24 25 26 27 28 ...
$ 19: int 20 21 22 23 24 25 26 27 28 29 ...
$ 20: int 21 22 23 24 25 26 27 28 29 30 ...
您甚至可以将其添加到一个函数中,并创建一种通用的双循环方式。
P.S。请记住,使用家族 apply
的任何函数都不是向量化的,但比 for-loop
.
效果更好
另一种方法是计算要比较的行的组合,然后使用 'mapply'。我假设您想一次比较矩阵中的两行:
# get combinations
cbn <- combn(nrow(your_data), 2) # take 2 at a time
ans <- mapply(dist_function
, your_data[cbn[1, ], 1]
, your_data[cbn[2, ], 1]
)
我在几条记录上制作了 paramStrings 的 df:
idName Str
1 Аэрофлот_Эконом 95111000210102121111010100111000100110101001
2 Аэрофлот_Комфорт 95111000210102121111010100111000100110101001
3 Аэрофлот_Бизнес 96111000210102121111010100111000100110101001
4 Трансаэро_Дисконт 26111000210102120000010100001010000010001000
5 Трансаэро_Туристический 26111000210002120000010100001010000010001000
6 Трансаэро_Эконом 26111000210002120000010100001010000010001000
现在我需要用一个 levenshtainDist 将每个与其他的进行比较,它作为一个函数(str1,str2)工作,所以我显然需要双循环。但是,我很确定应该有一种简洁的矢量化 (apply/lapply/sapply) 方法来做到这一点,但是我找不到任何类似的解决方案...
函数 adist
计算广义 Levenshtein 距离。这是你需要的吗?
假设您的数据在 data.frame 中,使用:adist(mydf$Str)
将 return 一个矩阵,其中每对 Str
列之间的距离。
因为你有一个 data.frame 我认为做双循环的最好方法是 lapply
/sapply
双循环,它与 data.frames
一起工作很好:
例如:
df1 <- data.frame(a=1:20,b=1:20) #example dataframe
a <- data.frame(lapply(1:nrow(df1), function(x) {
sapply(1:nrow(df1), function(y) {
sum( df1[x,2], df1[y,2]) #I just add the two cells (I only use the second column here for the demonstration) / replace with your function
}
)
}
)
)
colnames(a) <- 1:20 #change names
第一个 lapply
将 return nrow(df1)
列表,每个列表内将是一个 nrow(df1)
观察向量(函数的评估)。这意味着你将有一个 nrow(df1)
xnrow(df1)
列表,它可以很方便地转换为 data.frame
,就像我在上面所做的那样。因此你有一个 nrow(df1)
xnrow(df1)
data.frame
.
上面的输出:
> str(a)
'data.frame': 20 obs. of 20 variables:
$ 1 : int 2 3 4 5 6 7 8 9 10 11 ...
$ 2 : int 3 4 5 6 7 8 9 10 11 12 ...
$ 3 : int 4 5 6 7 8 9 10 11 12 13 ...
$ 4 : int 5 6 7 8 9 10 11 12 13 14 ...
$ 5 : int 6 7 8 9 10 11 12 13 14 15 ...
$ 6 : int 7 8 9 10 11 12 13 14 15 16 ...
$ 7 : int 8 9 10 11 12 13 14 15 16 17 ...
$ 8 : int 9 10 11 12 13 14 15 16 17 18 ...
$ 9 : int 10 11 12 13 14 15 16 17 18 19 ...
$ 10: int 11 12 13 14 15 16 17 18 19 20 ...
$ 11: int 12 13 14 15 16 17 18 19 20 21 ...
$ 12: int 13 14 15 16 17 18 19 20 21 22 ...
$ 13: int 14 15 16 17 18 19 20 21 22 23 ...
$ 14: int 15 16 17 18 19 20 21 22 23 24 ...
$ 15: int 16 17 18 19 20 21 22 23 24 25 ...
$ 16: int 17 18 19 20 21 22 23 24 25 26 ...
$ 17: int 18 19 20 21 22 23 24 25 26 27 ...
$ 18: int 19 20 21 22 23 24 25 26 27 28 ...
$ 19: int 20 21 22 23 24 25 26 27 28 29 ...
$ 20: int 21 22 23 24 25 26 27 28 29 30 ...
您甚至可以将其添加到一个函数中,并创建一种通用的双循环方式。
P.S。请记住,使用家族 apply
的任何函数都不是向量化的,但比 for-loop
.
另一种方法是计算要比较的行的组合,然后使用 'mapply'。我假设您想一次比较矩阵中的两行:
# get combinations
cbn <- combn(nrow(your_data), 2) # take 2 at a time
ans <- mapply(dist_function
, your_data[cbn[1, ], 1]
, your_data[cbn[2, ], 1]
)