匹配两个网格进行数据分析,对我的问题有一个很好的算法吗?

时间:2011-06-10 13:40:44

标签: python dataset comparison numpy scipy

我想在python中比较两个不同间距的数据集。我总是希望找到最接近的(最近邻居)匹配并重新调整数据,请参阅此示例:

数据集A:

ALTITUDE[m]   VALUE
1.            a1
2.            a2
3.            a3
4.            a4

数据集B:

ALTITUDE[m]   VALUE
0.7           b1
0.9           b2
1.7           b3
2.            b4
2.4           b5
2.9           b6
3.1           b7
3.2           b8
3.9           b9
4.1           b10

aibi包含双倍数字,但也包含nan个字段。

我想将数据集B转换为数据集A的高度网格,但由于数据集A包含的海拔高度低于数据集B,因此我想对它们进行平均。

ALTITUDE[m]   VALUE
1.            median(b1,b2)
2.            median(b3,b4,b5)
3.            median(b6,b7,b8)
4.            median(b9,b10)

即。已找到并平均最近的海拔高度。

相反,如果我想将数据集A与数据集B的网格匹配,则数据集A应该如下所示(最近邻居):

ALTITUDE[m]   VALUE
0.7           a1
0.9           a1
1.7           a2
2.            a2
2.4           a2
2.9           a3
3.1           a3
3.2           a3
3.9           a4
4.1           a4

也许这甚至有一个名字(我想这是一个常见的问题),但我不知道它,因此无法搜索它。我相信有一种有效的方法可以做到这一点,除了我自己编写的明显的解决方案(但我担心它不会有效,我会引入很多错误)。

最好使用numpy。

编辑:感谢您对所有四位贡献者的意见。我学到了一点而且我很抱歉没有问清楚。我自己在理解这个问题的过程中。你的答案指向我使用interp1danswer允许我为我滥用它。我将很快发布结果。我只接受一个答案,但任何人都会这样做。

5 个答案:

答案 0 :(得分:2)

两个假设: 1:您不是在寻找最近的邻居,而是在某个范围内的所有高度。所以,让我们说a1你想要所有的bn都在a1的0.5之内(根据你的例子给你b1和b2)。我会将“最近邻居”定义为不同的东西。

2:你的中位数并不算纳(根据某些IEEE惯例,numpy将它们视为无穷大,但这对我来说似乎很奇怪)。根据你的建议,我们使用scipy.stats的nanmedian。

我会做以下事情:

from numpy import *
from pylab import *

A_Alt = array([1,2,3,4])
A_Val = array([.33, .5, .6, .8])
B_Alt = array([.7, 0.9, 1.7, 2., 2.4, 2.9, 3.1, 3.2, 3.9, 4.1])
B_Val = array([.3, NaN, .8, .6, .7, .4, .3, NaN, .99, 1.3])

range = .5

B_Agrid = [nanmedian(B_Val[abs(B_Alt - k)<range]).item() for k in A_Alt]
A_Bgrid = [nanmedian(A_Val[abs(A_Alt - k)<range]).item() for k in B_Alt]    

我们发现A_Alt中B_Alt与k的距离小于指定范围的所有指数。然后我们取这些B_Val的中位数。 A_Bgrid的结果与请求的结果相同。

==编辑==

关于你最近邻居的不同假设: 让我们将最近的邻居作为具有最小绝对高度差的条目(或在条带情况下的条目),而不将nan作为值。注:这些结果与您的示例不符,因为b1因b2更接近而不是a1的最近邻居。

在此假设下,以下代码应该有效:

from numpy import *
from pylab import *
from scipy.stats import nanmedian

A_Alt = array([1,2,3,4])
A_Val = array([.33, .5, .6, .8])
B_Alt = array([.7, 0.9, 1.7, 2., 2.4, 2.9, 3.1, 3.2, 3.9, 4.1])
B_Val = array([.3, NaN, .8, .6, .7, .4, .3, NaN, .99, 1.3])

def ReGridMedian(AltIn, ValIn, AltOut):
    part = isfinite(ValIn)
    q = [abs(AltIn[part]-k) for k in AltOut]
    q = [nonzero(abs(k - min(k))<3*finfo(k.dtype).eps) for k in q]
    q = [ValIn[part][k] for k in q]
    return [median(k) for k in q]

B_Agrid = ReGridMedian(B_Alt, B_Val, A_Alt)    
A_Bgrid = ReGridMedian(A_Alt, A_Val, B_Alt)

我在一起攻击了一些东西,它检查两个值在机器精度内是否相同,但我认为有更好的方法。在任何情况下,我们首先过滤所有非nan的值,然后找到最接近的匹配,然后检查重复的最小值,然后得到这些值的中值。

====

这是否涵盖您的问题,还是我的假设不正确?

答案 1 :(得分:1)

查看numpy.interp

<击> http://docs.scipy.org/doc/numpy/reference/generated/numpy.interp.html

编辑numpy.interp仅提供线性插值,显然不是OP正在寻找的内容。而是使用像interp1d这样的scipy方法{{1 }})

http://docs.scipy.org/doc/scipy/reference/interpolate.html

您想要做的是使用一个数据集的高度点来插值另一个数据集的值。使用numpy方法或其中一种scipy插值方法可以非常轻松地完成此操作。

答案 2 :(得分:1)

这不是您正在寻找的答案,但这是我的50c答案......

A = {1:'a1',2:'a2',3:'a3',4:'a4'}
B = {0.7:'b1',0.9:'b2',1.7:'b3',2:'b4', 2.4:'b5'}

C = {} # result

# find altitude in A that is the closest to altitude in B
def findAltitude( altB,A):
    toto = [ ((alt-altB)**2,alt) for alt in A.keys() ]
    toto.sort()
    return toto[0][1]

#iter on each altitude of B
for altB,valueB in B.iteritems():
    altC = findAltitude( altB,A)
    if altC in C:
        C[altC].append(valueB)
    else:
        C[altC] = [valueB,]

# then do the median operation
#for altC,valueC in C.iteritems():
#   C[altC] = map( median, valueC ) # where median is your median function

print C

它根本不是最好的解决方案(特别是如果你有很多价值),但只能写得最快......

实际上,这取决于数据的存储方式。 Dictionnary不是最佳选择。

使用你的高度被分类的事实更有趣/更聪明。 您应该提供有关数据存储方式的更多详细信息(数组有numpy?)

====编辑====

我仍然不知道你的数据是怎样的,但是让我们根据你的海拔高度进行排序来尝试更“聪明”的东西。

from numpy import *
from pylab import *
from scipy.stats import nanmedian

# add val into C at the end of C or in the last place (depending if alt already exists in C or not)
def addto(C,val,alt):
    if C and C[-1][0]==alt:
        C[-1][1].append(valB)
    else:
        C.append( (alt,[valB,] ))



# values
A_Alt = array([1,2,3,4])
A_Val = array([.33, .5, .6, .8])
B_Alt = array([.7, 0.9, 1.7, 2., 2.4, 2.9, 3.1, 3.2, 3.9, 4.1])
B_Val = array([.3, NaN, .8, .6, .7, .4, .3, NaN, .99, 1.3])

#intermediate list of tuple (altitude, list_of_values)
C= []

#iterator on A
Aa = iter(A_Alt)
ainf = Aa.next()
asup = Aa.next()  # two first values of A_Alt

#iterator on B
Ba = iter(B_Alt)
Bv = iter(B_Val)

# regrid
try:
    while True:
        altB = Ba.next()
        valB = Bv.next()

        # find ainf and asup in A_Alt such that ainf < altB < asup
        while asup<altB:
            try:
                ainf,asup = asup, Aa.next()
            except StopIteration:
                break

        # find closest
        if abs(ainf-altB)<=abs(asup-altB):
            addto(C, valB, ainf)
        else:
            addto(C, valB, asup)

except StopIteration:
    pass

# do the median
res = [ nanmedian(k[1]) for k in C ] 

print res

然后想法迭代两个向量/高度列表,并且对于B的每个高度,找到围绕它的两个高度A.然后,很容易找到最接近的......

这比Daan的解决方案更不易读,但它应该更高效(数据大小呈线性)。

如果您的数据没有像那样存储,您只需要修改。

答案 3 :(得分:1)

这是一种方式:

import numpy as np

def regrid_op(x, y, xi, op=np.median):
    x, y, xi = np.atleast_1d(x, y, xi)
    if (x.ndim, y.ndim, xi.ndim) != (1, 1, 1):
        raise ValueError("works only for 1D data")

    yi = np.zeros(xi.shape, dtype=y.dtype)
    yi.fill(np.nan)

    # sort data
    j = np.argsort(x)
    x = x[j]
    y = y[j]

    # group items by nearest neighbour
    x0s = np.r_[xi, np.inf]
    xc = .5*(x0s[:-1] + x0s[1:])

    j0 = 0
    for i, j1 in enumerate(np.searchsorted(x, xc)):
        print "x =", xi[i], ", y =", y[j0:j1] # print some debug info
        yi[i] = op(y[j0:j1])
        j0 = j1

    return yi

xi = np.array([1, 2, 3, 4])
x = np.array([0.7, 0.9, 1.7, 2.0, 2.4, 2.9, 3.1, 3.2, 3.9, 4.1])
y = np.array([1,   2,   3,   4,   5,   6,   7,   8,   9,   10.])

print regrid_op(x, y, xi)

我没有看到在xi数组中对项进行矢量化的方法,所以如果网格A中的点数不是太大,这应该是有效的。

编辑:这也假定xi中的点已排序。

答案 4 :(得分:0)

覆盖第二种情况的一种方法(网格B到A,即从几个高度到多个高度)是这样的:

外推功能(来自here

from scipy.interpolate import interp1d

def extrap1d(interpolator):
    xs = interpolator.x
    ys = interpolator.y

    def pointwise(x):
        if x < xs[0]:
            return ys[0]
        elif x > xs[-1]:
            return ys[-1]
        else:
            return interpolator(x)

    def ufunclike(xs):
        return array(map(pointwise, array(xs)))

    return ufunclike

A_Alt = array([1,2,3,4])
A_Val = array([.33, .5, .6, .8])
B_Alt = array([.7, 0.9, 1.7, 2., 2.4, 2.9, 3.1, 3.2, 3.9, 4.1])

实际重新划分:

f_i = interp1d(A_Alt, A_Val, kind='nearest')
f_x = extrap1d(f_i)

f_x(B_Alt)

输出:

array([ 0.33,  0.33,  0.5 ,  0.5 ,  0.5 ,  0.6 ,  0.6 ,  0.6 ,  0.8 ,  0.8 ])