我有两个包含相同元素的数组,但顺序不同,我想知道它们的订单差异程度。
我试过的方法不起作用。它如下:
对于每个列表,我构建了一个矩阵,该矩阵为每对元素记录它们在列表中是高于还是低于对方。然后我计算了这两个矩阵的皮尔森相关系数。这非常糟糕。这是一个简单的例子:
list 1: 1 2 3 4 list 2: 1 3 2 4
我上面描述的方法产生了这样的矩阵(其中1表示行号高于列,0表示反之亦然):
list 1: 1 2 3 4 1 1 1 1 2 1 1 3 1 4 list 2: 1 2 3 4 1 1 1 1 2 0 1 3 1 4
由于唯一的区别是元素2和3的顺序,因此应视为非常相似。这两个矩阵的Pearson相关系数为0,表明它们根本没有相关性。我想问题是我正在寻找的不是真正的相关系数,而是一些其他类型的相似性度量。编辑距离,也许?
有人可以提出更好的建议吗?
答案 0 :(得分:11)
每个元素的指数差异的均方。
List 1: A B C D E
List 2: A D C B E
列表2中列表1的每个元素的索引(从零开始)
A B C D E
0 3 2 1 4
列表1中列表1的每个元素的索引(从零开始)
A B C D E
0 1 2 3 4
差异:
A B C D E
0 -2 0 2 0
差异之处:
A B C D E
4 4
平均差异= 8/5。
答案 1 :(得分:2)
只是一个想法,但是在调整标准排序算法以计算将list1转换为list2所需的交换操作数量方面是否有任何影响?
我认为定义比较函数可能很困难(甚至可能与原始问题一样困难!),这可能效率低下。
编辑:考虑到这一点,比较函数基本上由目标列表本身定义。例如,如果列表2是:
1 4 6 5 3
...然后比较函数应该导致1< 4< 6< 5< 3(并返回条目相等的相等)。
然后只需要扩展交换功能来计算交换操作。
答案 2 :(得分:1)
您可能会考虑将一个字符串转换为另一个字符串所需的更改次数(我猜您在提到编辑距离时就是这样做了。)
请参阅:http://en.wikipedia.org/wiki/Levenshtein_distance
虽然我认为l-distance不考虑旋转。如果允许旋转作为操作,则:
1,2,3,4
和
2,3,4,1
非常相似。
答案 3 :(得分:1)
这里的派对有点晚了,但仅仅是为了记录,我认为Ben几乎拥有它...如果你进一步研究相关系数,我想你已经发现 Spearman的等级相关系数可能是最佳选择。
有趣的是,jamesh似乎已经得出了类似的衡量标准,但没有标准化。
请参阅此recent SO answer。
答案 4 :(得分:0)
有一个分支定界算法可以适用于您喜欢的任何运算符集。它可能不是很快。伪代码就是这样的:
bool bounded_recursive_compare_routine(int* a, int* b, int level, int bound){
if (level > bound) return false;
// if at end of a and b, return true
// apply rule 0, like no-change
if (*a == *b){
bounded_recursive_compare_routine(a+1, b+1, level+0, bound);
// if it returns true, return true;
}
// if can apply rule 1, like rotation, to b, try that and recur
bounded_recursive_compare_routine(a+1, b+1, level+cost_of_rotation, bound);
// if it returns true, return true;
...
return false;
}
int get_minimum_cost(int* a, int* b){
int bound;
for (bound=0; ; bound++){
if (bounded_recursive_compare_routine(a, b, 0, bound)) break;
}
return bound;
}
它所花费的时间在答案中大致呈指数级,因为它由最后一个有效的边界支配。
补充:这可以扩展为查找存储在trie中的最近匹配字符串。我几年前用拼写校正算法做过。
答案 5 :(得分:0)
我不确定它在引擎盖下使用的确切公式,但difflib.SequenceMatcher.ratio()
正是如此:
ratio(self) method of difflib.SequenceMatcher instance:
Return a measure of the sequences' similarity (float in [0,1]).
代码示例:
from difflib import SequenceMatcher
sm = SequenceMatcher(None, '1234', '1324')
print sm.ratio()
>>> 0.75
答案 6 :(得分:0)
基于一点mathematics的另一种方法是计算反转次数以将其中一个数组转换为另一个数组。 反转是两个相邻数组元素的交换。在红宝石中它是这样做的:
# extend class array by new method
class Array
def dist(other)
raise 'can calculate distance only to array with same length' if length != other.length
# initialize count of inversions to 0
count = 0
# loop over all pairs of indices i, j with i<j
length.times do |i|
(i+1).upto(length) do |j|
# increase count if i-th and j-th element have different order
count += 1 if (self[i] <=> self[j]) != (other[i] <=> other[j])
end
end
return count
end
end
l1 = [1, 2, 3, 4]
l2 = [1, 3, 2, 4]
# try an example (prints 1)
puts l1.dist(l2)
两个长度为n的数组之间的距离可以在0(它们是相同的)和n *(n + 1)/ 2之间(反转第一个数组得到第二个)。如果您希望距离始终在0和1之间,以便能够比较不同长度的数组对的距离,则除以n *(n + 1)/ 2.
该算法的缺点是运行时间为n ^ 2。它还假设数组没有双重条目,但可以进行调整。
关于代码行“count + = 1 if ......”的注释:只有当第一个列表的第i个元素小于小于第j个时,才会增加计数元素和第二个列表的第i个元素比其第j个元素更大,反之亦然(意味着第一个列表的第i个元素大于第j个元素,第二个列表的第i个元素小于第j个元素。简而言之:(l1 [i]&lt; l1 [j]和l2 [i]> l2 [j])或(l1 [i]> l1 [j]和l2 [i]&lt; l2 [j])
答案 7 :(得分:0)
如果有两个订单,则应查看两个重要的排名相关系数:
斯皮尔曼等级相关系数:https://en.wikipedia.org/wiki/Spearman%27s_rank_correlation_coefficient
这与Jamesh答案几乎相同,但在-1到1的范围内缩放。
它被定义为:
1 - (6 * sum_of_squared_distances)/(n_samples *(n_samples ** 2 - 1)
Kendalls tau:https://nl.wikipedia.org/wiki/Kendalls_tau
使用python时可以使用:
from scipy import stats
order1 = [ 1, 2, 3, 4]
order2 = [ 1, 3, 2, 4]
print stats.spearmanr(order1, order2)[0]
>> 0.8000
print stats.kendalltau(order1, order2)[0]
>> 0.6667