无复杂性的Kolmogorov(-Smirnov)


12

在统计中,有时了解两个数据样本是否来自相同的基础分布很有用。一种实现方法是使用两个样本的Kolmogorov-Smirnov检验

您的任务是编写一个程序,该程序读取两个未排序的非负整数数组,并计算测试中使用的主要统计量。


给定一个数组A和一个实数x,定义分布函数F

F(A,x) = (#number of elements in A less than or equal to x)/(#number of elements in A)

给定两个数组A1A2,定义

D(x) = |F(A1, x) - F(A2, x)|

两次抽样的Kolmogorov-Smirnov统计量是D全部real 的最大值x

A1 = [1, 2, 1, 4, 3, 6]
A2 = [3, 4, 5, 4]

然后:

D(1) = |2/6 - 0| = 1/3
D(2) = |3/6 - 0| = 1/2
D(3) = |4/6 - 1/4| = 5/12
D(4) = |5/6 - 3/4| = 1/12
D(5) = |5/6 - 4/4| = 1/6
D(6) = |6/6 - 4/4| = 0

两个数组的KS统计量为1/2的最大值D

测试用例

[0] [0] -> 0.0
[0] [1] -> 1.0
[1, 2, 3, 4, 5] [2, 3, 4, 5, 6] -> 0.2
[3, 3, 3, 3, 3] [5, 4, 3, 2, 1] -> 0.4
[1, 2, 1, 4, 3, 6] [3, 4, 5, 4] -> 0.5
[8, 9, 9, 5, 5, 0, 3] [4, 9, 0, 5, 5, 0, 4, 6, 9, 10, 4, 0, 9] -> 0.175824
[2, 10, 10, 10, 1, 6, 7, 2, 10, 4, 7] [7, 7, 9, 9, 6, 6, 5, 2, 7, 2, 8] -> 0.363636

规则

  • 您可以编写函数或完整程序。输入可以通过STDIN或函数自变量,输出可以通过STDOUT或返回值。
  • 您可以假定输入的任何明确列表或字符串格式,只要两个数组都一致
  • 如果您的语言对此具有内置功能,那么您可能不使用它。
  • 答案至少必须是3位有效数字
  • 这是,因此以最少字节数赢得程序

所有输入都是整数数组,还是可以包含浮点数?
kennytm

@KennyTM只是非负整数。我以为我会保持简单。
Sp3000

我们可以假设数组有最大值吗?(例如,所有的条目A都在下面length(A)?)
瑕疵的

@flawr不,您不能假设最大值
Sp3000

我喜欢这个标题。我只是针对kolmogorov复杂度袋,但这一次不行。
edc65

Answers:


10

杀伤人员地雷(29 24)

(感谢Zgarb的额外启发。)

{⌈/|-⌿⍺⍵∘.(+/≤÷(⍴⊣))∊⍺⍵}

该函数将数组作为左参数和右参数。

      8 9 9 5 5 0 3 {⌈/|-⌿⍺⍵∘.(+/≤÷(⍴⊣))∊⍺⍵} 4 9 0 5 5 0 4 6 9 10 4 0 9 
0.1758241758

说明:

{⌈/                                maximum of
   |                               the absolute value of
    -⌿                             the difference between
      ⍺⍵∘.(         )∊⍺⍵          for both arrays, and each element in both arrays
            +/≤                    the amount of items in that array ≤ the element
               ÷                   divided by
                (⍴⊣)              the length of that array
                          }

我不知道你能做到⍺⍵!那很方便。
Zgarb 2015年

1
另外,我认为这⍳⌈/是不必要的,因为最大值是在数组值之一准确获得的。
Zgarb

@Zgarb:您当然是对的,我只需要测试每个可能的数组值。那意味着我也可以摆脱0,它,因为如果数组包含它,它将对此进行测试。谢谢!(这将教给我,就像通常情况下,如果您必须添加一个特殊情况,这意味着该算法还不够简单。)
marinus

2
这就是真正的法术。
史蒂文·卢

@ Sp3000:您是否正确编写了一个元素数组?您不能只是写1,因为那将是一个标量。您应该改写(,1)。如果这样做,它会起作用。
marinus

4

J 39

我敢肯定可以缩短更多时间

f=:+/@|:@(>:/)%(]#)
>./@:|@((,f])-(,f[))

用法

2 10 10 10 1 6 7 2 10 4 7 >./@:|@((,f])-(,f[)) 7 7 9 9 6 6 5 2 7 2 8
0.363636

这会创建函数还是使用stdin / stdout?第二部分到底做什么?(对于函数调用来说看起来有点长吗?)
瑕疵的,2015年

@flawr一个功能,类似于APL
沙沙

我认为您可以避免明确定义f是否使用类似方法,>./@:|@({.-{:)f"1@,但我不太确定。
FUZxxl 2015年

4

蟒蛇3,132 108 95 88

f=lambda a,x:sum(n>x for n in a)/len(a)
g=lambda a,b:max(abs(f(a,x)-f(b,x))for x in a+b)

输入是该功能的2个列表 g

感谢:Sp3000,xnor,undergroundmonorail

第2行,第一个调用f类似于“传真”。我发现这很有趣


2
要计算满足某个属性的列表中元素的数量,这要简短些sum(n>x for n in a)。另外,您似乎没有使用s=filter。对于max,您实际上不需要列表括号;Python让函数parens成为理解parens的两倍。
xnor 2015年

谢谢!我filter在以前的版本中使用过,忘了删除它。遗憾的是,我无法删除第一对方括号,因为那将是没有的生成器len
Kroltan

您不需要len,请再次阅读评论:P
地下

3

的JavaScript(ES6)99 119 128

或多或少简单的JavaScript实现,可能更容易实现。在F函数中,我使用>而不是<=,作为abs(F(a)-F(b))=== abs((1-F(a))-(1-F(b)))

在上一次编辑中,没有更多的函数定义作为默认参数。

正如我所说,这很简单。F函数是F函数,D函数是第2行中使用的未命名函数。对于两个数组中存在的每个值,均使用.map进行评估,因为all实数的最大值必须是其中之一。最后,使用扩展运算符(...)将D值数组作为参数列表传递给max函数。

K=(a,b)=>Math.max(...a.concat(b).map(x=>
  Math.abs((F=a=>a.filter(v=>v>x).length/a.length)(a)-F(b))
))

在FireFox / FireBug控制台中测试

;[[[0],[0]], [[0],[1]],
[[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]],
[[3, 3, 3, 3, 3],[5, 4, 3, 2, 1]],
[[1, 2, 1, 4, 3, 6],[3, 4, 5, 4]],
[[8, 9, 9, 5, 5, 0, 3],[4, 9, 0, 5, 5, 0, 4, 6, 9, 10, 4, 0, 9]],
[[2, 10, 10, 10, 1, 6, 7, 2, 10, 4, 7],[7, 7, 9, 9, 6, 6, 5, 2, 7, 2, 8]]]
.forEach(x=>console.log(x[0],x[1],K(x[0],x[1]).toFixed(6)))

输出量

[0] [0] 0.000000
[0] [1] 1.000000
[1, 2, 3, 4, 5] [2, 3, 4, 5, 6] 0.200000
[3, 3, 3, 3, 3] [5, 4, 3, 2, 1] 0.400000
[1, 2, 1, 4, 3, 6] [3, 4, 5, 4] 0.500000
[8, 9, 9, 5, 5, 0, 3] [4, 9, 0, 5, 5, 0, 4, 6, 9, 10, 4, 0, 9] 0.175824
[2, 10, 10, 10, 1, 6, 7, 2, 10, 4, 7] [7, 7, 9, 9, 6, 6, 5, 2, 7, 2, 8] 0.363636

我对您的函数很好奇K:您F,D在参数列表中定义其他函数是否正确?这是否像某些可选参数一样?
瑕疵的

@flawr是的,它们是带有默认值的可选参数。因此,避免了对全局变量空间的污染(这在代码高尔夫中不是问题,但无论如何……)
edc65

1
另外,由于该函数已经需要2个变量(因此用括号括起来),所以将这些变量移出选项var列表到函数主体内部将增加2个字节。
Optimizer

2

CJam,33 31字节

q~_:+f{\f{f<_:+\,d/}}z{~-z}%$W=

输入是两个数组的CJam样式数组。

例:

[[8 9 9 5 5 0 3] [4 9 0 5 5 0 4 6 9 10 4 0 9]]

输出:

0.17582417582417587

在这里在线尝试


2

Matlab (121)(119)

这是一个程序,它通过stdin接收两个列表,并将结果打印到stdout。这是一个不错的选择,我尝试了尽可能多地打高尔夫球。K(a)返回一个计算的函数x -> F(a,x)。然后,@(x)abs(g(x)-h(x))将与该函数相对应的匿名函数D应用于的每个可能整数,0:max([a,b])并显示结果的最大值。(arrayfunmap其他语言相同:将函数应用于数组的每个元素)

a=input('');b=input('');
K=@(a)@(x)sum(a<=x)/numel(a);
g=K(a);h=K(b);
disp(max(arrayfun(@(x)abs(g(x)-h(x)),0:max([a,b]))))

2

Erlang,96个字节

edc65的JavaScript解决方案已移植到Erlang。

f(A,B)->F=fun(A,X)->length([V||V<-A,V>X])/length(A)end,lists:max([abs(F(A,X)-F(B,X))||X<-A++B]).

测试:

lists:foreach(fun ([H,T] = L) -> io:format("~p ~p~n", [L, w:f(H, T)]) end, [[[0],[0]], [[0],[1]],
        [[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]],
        [[3, 3, 3, 3, 3],[5, 4, 3, 2, 1]],
        [[1, 2, 1, 4, 3, 6],[3, 4, 5, 4]],
        [[8, 9, 9, 5, 5, 0, 3],[4, 9, 0, 5, 5, 0, 4, 6, 9, 10, 4, 0, 9]],
        [[2, 10, 10, 10, 1, 6, 7, 2, 10, 4, 7],[7, 7, 9, 9, 6, 6, 5, 2, 7, 2, 8]]]).

输出:

[[0],[0]] 0.0
[[0],[1]] 1.0
[[1,2,3,4,5],[2,3,4,5,6]] 0.20000000000000007
[[3,3,3,3,3],[5,4,3,2,1]] 0.4
[[1,2,1,4,3,6],[3,4,5,4]] 0.5
[[8,9,9,5,5,0,3],[4,9,0,5,5,0,4,6,9,10,4,0,9]] 0.17582417582417587
[[2,10,10,10,1,6,7,2,10,4,7],[7,7,9,9,6,6,5,2,7,2,8]] 0.36363636363636365

2

斯塔塔215

因为STATA已经具有ksmirnov命令,所以90%的输入格式都可以使用。

di _r(a)
di _r(b)
file open q using "b.c",w
forv x=1/wordcount($a){
file w q "1,"(word($a,`x'))_n
}
forv x=1/wordcount($b){
file w q "2,"(word($b,`x'))_n
}
file close q
insheet using "b.c"
ksmirnov v2,by(v1)
di r(D)

哦,我不认为语言会为此内置函数...我只是做了一些研究,我认为从现在起最好禁止内置函数,但是您可以保留它,因为它是在规则发布之前发布的变化:)
Sp3000

2

R,65个字节

f=function(a,b){d=c(a,b);e=ecdf(a);g=ecdf(b);max(abs(e(d)-g(d)))}

此函数以两个向量作为参数,并返回其经验累积分布函数的最大差。

如果允许内置,它将减少到仅12个字节:

ks.test(a,b)

1

Mathematica,76 73 63

Mathematica具有内置功能KolmogorovSmirnovTest,但是我在这里不使用它。

k=N@MaxValue[Abs[#-#2]&@@(Tr@UnitStep[x-#]/Length@#&/@{##}),x]&

用法:

k[{1, 2, 1, 4, 3, 6}, {3, 4, 5, 4}]

0.5


0

在Python 3.4.2中快速实现(79个字节):

F=lambda A,x:len([n for n in A if n<=x])/len(A)
D=lambda x:abs(F(A1,x)-F(A2,x))

例:

>>> A1 = [-5, 10, 8, -2, 9, 2, -3, -4, -4, 9]
>>> A2 = [-5, -3, -10, 8, -4, 1, -7, 6, 9, 5, -7]
>>> D(0)
0.045454545454545414

1
要求是为x的所有整数值找到D(x)的最大值。请遵守问题规范。
Optimizer

1
欢迎!正如Optimizer所说,任务是找到的最大值D,而不仅仅是实现D一个函数。另外,我很抱歉,如果我还不清楚,但你不能假设A1A2已经定义的变量(你可以把他们在lambda不过,如lambda x,A1,A2:-没关系)
SP3000

另外,我还添加了一些语法突出显示-我认为它看起来更漂亮:)
Sp3000

抱歉,我是新来的。
卡彭

没问题:)如果不清楚,可以在评论中提问。但是再次欢迎您!
Sp3000

0

Java- 633622字节

好吧,首先,尝试在Java上做得更好,这就是为什么我在Java中尝试过它,我知道我永远做不到,但是,它的乐趣。第二,老实说,我可以用更少的方式做到这一点,然后到了遍地都是双打的阶段,并且方法声明意味着使用方法总共只能节省4-5个字符。简而言之,我是一个糟糕的高尔夫球手。

编辑:用法格式> java K“ 2,10,10,10,1,6,7,2,10,4,7”“ 7,7,9,9,6,6,5,2,7,2 ,8“

import java.lang.*;
class K{public static void main(String[]a){double[]s1=m(a[0]);double[]s2=m(a[1]);
int h=0;if(H(s1)<H(s2))h=(int)H(s2);else h=(int)H(s1);double[]D=new double[h];
for(int i=0;i<h;i++){D[i]=Math.abs(F(s1,i)-F(s2,i));}System.out.println(H(D));}
static double[]m(String S){String[]b=S.split(",");double[]i=new double[b.length];
for(int j=0;j<b.length;j++){i[j]=new Integer(b[j]);}return i;}
static double H(double[]i){double t=0;for(int j=0;j<i.length;j++)
{if(i[j]>t)t=i[j];}return t;}
static double F(double[]A,int x){double t=0;double l=A.length;
for(int i=0;i<l;i++){if(A[i]<=x)t++;}return t/l;}}

你是对的。更新。
Bryan Devaney

0

哈斯克尔96 83

l=fromIntegral.length
a%x=l(filter(<=x)a)/l a
a!b=maximum$map(\x->abs$a%x-b%x)$a++b

(!)是kolmogorov-smirnov函数,它包含两个列表


1
一些快速高尔夫:使用map而不是fmap; 使用maximum而不是foldr1 max; 定义l=fromIntegral.length,您可以删除它i,然后可以缩短%l(filter(<=x)a)/l a。降低到84!
MtnViewMark

0

R,107个字节

不同的方法

f=function(a,b){e=0
d=sort(unique(c(a,b)))
for(i in d-min(diff(d))*0.8)e=max(abs(mean(a<i)-mean(b<i)),e)
e}

不打高尔夫球

f=function(a,b){
    e=0
    d=sort(unique(c(a,b)))
    d=d-min(diff(d))*0.8
    for(i in d) {
        f=mean(a<i)-mean(b<i)
        e=max(e,abs(f))
    }
    e
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.