2016-07-25 81 views
1

我有一个值和零矩阵,其中零= NA。这些值散布在矩阵的周围,我想要做的是插值所有NA值的值。这是数据:R:为什么矩阵3d线性插值不正确?

enter image description here

我试图通过采取在我的矩阵中所有已知值,并通过距离值乘以猜测所有这些值(使得更远的一点是,它的影响力就越小)。这是内插结果是什么样子: enter image description here

正如你所看到的,这种方法不是很有效,它影响NA s到已知值最接近的,但后来他们迅速收敛到平均值。我认为这是因为它采用了整个范围,这个范围有许多起伏......而不仅仅是距离它最近的点。

显然,矩阵运算并不是我的专业......我需要改变以正确执行线性插值?

下面的代码:

library(dplyr) 
library(plotly) 

Cont <- structure(c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 1816, 2320, 1406, 2028, 1760, 1932, 1630, 
        1835, 1873, 1474, 1671, 2073, 1347, 2131, 2038, 1969, 2036, 1602, 
        1986, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 2311, 1947, 2094, 1947, 2441, 1775, 1461, 1260, 
        1494, 2022, 1863, 1587, 2082, 1567, 1770, 2065, 1404, 1809, 1972, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 2314, 1595, 2065, 1870, 2178, 1410, 1994, 1979, 2111, 
        1531, 1917, 1559, 2109, 1921, 1606, 1469, 1601, 1771, 1771), .Dim = c(19L, 
                         30L)) 

    ## First get real control values 
    idx <- which(Cont > 0, arr.ind=TRUE) 
    V <- Cont[idx] 
    ControlValues <- data.frame(idx,V) 

    ## Make data.frame of values to fill 
    toFill <- which(Cont == 0, arr.ind=TRUE) %>% as.data.frame 
    toFill$V <- 0 

    ## And now figure out the weighted value of each point 
    for (i in 1:nrow(toFill)){ 
    toFill[i,] -> CurrentPoint 

    Xs <- (1/abs(CurrentPoint[,1] - ControlValues[,1])) 
    Xs[is.infinite(Xs)] <- 0 
    Xs <- Xs/sum(Xs)/100 

    Ys <- (1/abs(CurrentPoint[,2] - ControlValues[,2])) 
    Ys[is.infinite(Ys)] <- 0 
    Ys <- Ys/sum(Ys)/100 

    ControlValues1 <- data.frame(Xs,Ys) 
    toFill[i,3] <- sum(rowMeans(ControlValues1) * ControlValues$V)*100 
    } 

    ## add back in the controls and reorder 
    bind_rows(ControlValues,toFill) -> Both 
    Both %>% arrange(row,col) -> Both 

    ## and plot the new surface 
    NewCont <- matrix(Both$V,max(Both$row),max(Both$col),byrow = T) 
    plot_ly(z=NewCont, type="surface",showscale=FALSE) 
+0

你赢了不能*插值* x <10的值,因为您的数据在那里没有支持。如果您只对范围“10 <= x <= 30”的插值值感兴趣,则可以使用双线性插值。 – aichao

+0

公平的一点。我想插入和外插。是不是我做双线性插值? –

+0

我不认为你的代码做双线性插值。此外,公正地警告说,由于您的数据非常稀少,因此您案例中的推断几乎没有价值。 – aichao

回答

1

一种方法来内插和外推在R数据是使用akima包。以下执行双线性插值,然后使用数据框ControlValues中的已知数据点作为输入来外插以填充Cont中的零。

library(akima) 
library(plotly) 

NewCont <- akima::interp(x=ControlValues[,1], y=ControlValues[,2], z=ControlValues[,3], 
         xo=1:nrow(Cont), yo=1:ncol(Cont), linear=TRUE)$z 
NewCont[,1:9] <- akima::interp.old(x=ControlValues[,1], y=ControlValues[,2], 
            z=ControlValues[,3], xo=1:nrow(Cont), 
            yo=1:9, ncp=2, extrap=TRUE)$z 

plot_ly(z=NewCont, type="surface",showscale=FALSE) 

注:

  1. akima::interp第一呼叫执行双线性内插。有关用法和详细信息,请参阅帮助页面?akima::interp

    • 的一个关键点在于,输入xyz为已知数据点不必是在x-y网格。在这种情况下,这些是ControlValues的列。
    • akima::interp输出是一个列表,其z组分是内插的值超过其xy坐标由输入xoyo,分别被定义在网格的矩阵。在这种情况下,这些只是Cont
    • 的行和列索引正如在帮助页面的凸包外点

    z值返回NA说。

    在这种情况下,对应于yo=1:9的输出的第一九列将是NA秒。

  2. akima::interp(实际上akima::interp.old)第二呼叫执行数据外推来填充在由第一呼叫离开NA秒。有关此用法的详细信息,请参见this SO quation/answer

上述方法给出以下结果

NewCont

执行双线性内插是使用interp.surface函数在fields包的另一种方法。提到这种方法是因为实现是一个R脚本,可以通过在R命令行输入函数名称interp.surface来列出该脚本。

library(fields) 

loc <- make.surface.grid(list(x=1:nrow(Cont), y=1:ncol(Cont))) 
NewCont2 <- matrix(interp.surface(list(x=sort(unique(ControlValues[,1])), 
             y=sort(unique(ControlValues[,2])), 
             z=matrix(ControlValues[,3], 
               nrow=length(unique(ControlValues[,1])), 
               ncol=length(unique(ControlValues[,2])))), 
            loc), nrow=nrow(Cont), ncol=ncol(Cont)) 
NewCont2[,1:9] <- akima::interp.old(x=ControlValues[,1], y=ControlValues[,2], 
            z=ControlValues[,3], xo=1:nrow(Cont), 
            yo=1:9, ncp=2, extrap=TRUE)$z 

这里的要求与akima::interp的要求相反。具体而言,已知数据点必须位于网格上。然而,内插坐标不需要在网格上,而是包含对应的列向量xy坐标的矩阵,其中每个元组(x[i],y[i])是要内插的坐标。由于ControlValues中的数据点位于网格上,因此这些情况下的这些要求也得到满足。有关用法和详细信息,请参阅帮助页面?interp.surface

注:

  1. sort(unique(ControlValues[,1]))sort(unique(ControlValues[,2]))简单地给出了xy坐标已知数据点的网格
  2. 该列表中的z组件是简单地用于再成形为已知数据点的z值在已知数据点的网格上的矩阵
  3. 要内插的坐标矩阵由make.surface.grid使用为xy坐标Conf的行和列索引,分别
  4. 坐标进行内插的是位于已知点的网格的外部将导致NA
  5. interp.surface一个内插的值返回对应于坐标进行内插z值的矢量。这是然后在坐标格内插,它通过ncol(Cont)

最后的尺寸为nrow(Cont) rehaped一个矩阵,很容易验证两种方法产生相同的结果

print(max(abs(NewCont - NewCont2))) 
##[1] 4.547474e-13 
+0

优秀的答案。我甚至会问如何比较插值方法,但最后你甚至会展示这一点很棒。精彩!令人难以置信的是,这两种方法如此接近......我猜插值算法非常相似?我注意到的另一件事情,有点令人讨厌的是,山峰和山谷太极端了......如果我对构建基线控制表面感兴趣,也许我应该首先应用一些平滑处理。有没有一些正确的方法来做到这一点?或者把我的所有数值乘以0.8左右是否公平?再次感谢! –

+0

@AmitKohli对于迟到回复你抱歉。对于第一个问题,简短的答案是'akima'中的算法更复杂,因为它不需要已知点的网格进行插值。但是,'fields'中的算法是人们通常称为双线性插值的算法。这需要一个已知点的网格,数据恰好满足。关于第二个问题的答案,请参阅我的下一个评论。 – aichao

+0

@AmitKohli正确的方法都取决于您的数据的含义。如果你的数据被认为是完全准确的,那么你应该插入(并可以外推)。为了获得更高的平滑度,可能需要使用三次样条等高阶函数进行插值。如果您的数据被认为是嘈杂的,那么您希望为这些数据点拟合一些函数,以最小化某些错误标准。这是估计。这里,函数可以是线性的或者更高阶的。不同之处在于拟合通常不会保留估计函数表示的数据值。 – aichao