Python中多级线性插值的有效方法

时间:2019-04-30 18:26:13

标签: python pandas numpy interpolation

我目前正在一个项目中,以估算流量计的不确定性。仪表不确定度基于四个不同的值:

  1. 液体流量(liq)
  2. 流体粘度(cP)
  3. 水液比(wlr)
  4. 气体体积分数(gvf)

第三方为电表提供了liq,cP,wlr和gvf的多个不同值的表。您可以猜测,仪表的数据永远不会完美地落入预定义的值之一。例如,一分钟的数据可能显示为:

  1. 液体流量:6532
  2. 流体粘度:22
  3. 水液比:0.412
  4. 气体体积分数:0.634

对上面的数据进行四次插值,以找出不确定性。

我想出了一个解决方案,但似乎很笨拙,我想知道是否有人有任何想法。我还是熊猫游戏的新手,非常感谢看到其他人的解决方案。

最初,我对数据进行排序,以将表缩小为要查找的实际值之上和之下的值。

aliq = 6532 # stbpd
avisc = 22 # centipoise
awlr = 0.412 # water liquid ratio
agvf = 0.634 # gas volume fraction

def findclose(num, colm):

    arr = colm.unique()

    if num in arr:
        clslo = num
        clshi = num

    else:
        clslo = arr[arr > num].min() # close low value
        clshi = arr[arr < num].max() # close high value

    return [clslo, clshi]

df = tbl_vx52[
    (tbl_vx52['liq'].isin(findclose(aliq,tbl_vx52['liq']))) & 
    (tbl_vx52['visc'].isin(findclose(avisc,tbl_vx52['visc']))) & 
    (tbl_vx52['wlr'].isin(findclose(awlr,tbl_vx52['wlr']))) & 
    (tbl_vx52['gvf'].isin(findclose(agvf,tbl_vx52['gvf'])))
                   ].reset_index(drop=True)

该表从2240减少到16。而不是包含所有数据(tbl_vx52)。我已经创建了一些要加载的代码,因此您可以看到子数据框的外观,称为df,仅包含此示例区域上方和下方的值。

df = pd.DataFrame({'liq':[5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 7000, 7000, 7000, 7000, 7000, 7000, 7000, 7000],
                  'visc':[10, 10, 10, 10, 30, 30, 30, 30, 10, 10, 10, 10, 30, 30, 30, 30],
                  'wlr':[0.375, 0.375, 0.5, 0.5, 0.375, 0.375, 0.5, 0.5, 0.375, 0.375, 0.5, 0.5, 0.375, 0.375, 0.5, 0.5],
                  'gvf':[0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75],
                  'uncert':[0.0707, 0.0992, 0.0906, 0.1278, 0.0705, 0.0994, 0.091, 0.128, 0.0702, 0.0991, 0.0905, 0.1279, 0.0704, 0.0992, 0.0904, 0.1283],
                  })

完成了一些非常粗略的循环,以开始基于各个输入(liq,visc,wlr或gvf)对值进行配对。下面显示的是gvf上的第一个循环。

pairs = [
    slice(0,1),
    slice(2,3),
    slice(4,5),
    slice(6,7),
    slice(8,9),
    slice(10,11),
    slice(12,13),
    slice(14,15)]

for pair in pairs:

    df.loc[pair,'uncert'] = np.interp(
        agvf, 
        df.loc[pair,'gvf'], 
        df.loc[pair,'uncert']
    )
    df.loc[pair,'gvf'] = agvf

df = df.drop_duplicates().reset_index(drop=True)

删除重复的值,从16行减少到8行。然后再对wlr重复一次。

pairs = [
    slice(0,1),
    slice(2,3),
    slice(4,5),
    slice(6,7)
]

for pair in pairs:

    df.loc[pair,'uncert'] = np.interp(
        awlr, 
        df.loc[pair,'wlr'], 
        df.loc[pair,'uncert']
    )
    df.loc[pair,'wlr'] = awlr

df = df.drop_duplicates().reset_index(drop=True)

对visc(四行)重复上述结构,最后对液体(两行)重复,直到子数组中只剩下一个值。这样就可以在您的工作点获得米的不确定性。

我知道它很笨重。对于不同方法的任何投入或想法,我们将不胜感激。

1 个答案:

答案 0 :(得分:1)

好的,我能够找到并应用基于矩阵的解决方案。它基于三线性插值的矩阵方法,可以扩展为四线性插值。维基百科在trilinear interpolation上提供了很好的文章。维基百科文章中的8x8矩阵可以扩展为16x16,以进行四线性插值。下面编写了一个函数,以使矩阵内的每一行成为可能。

def quad_row(x, y, z, k):
    """
    Generate a row for the quad interpolation matrix
    x, y, z, k are scalar input values
    """    
    qrow = [1, 
            x, y, z, k, 
            x*y, x*z, x*k, y*z, y*k, z*k,
            x*y*z, x*y*k, x*z*k, y*z*k, 
            x*y*z*k]
    return qrow

显然,这只是三线性矩阵内行的扩展。该函数可以循环十六次以生成整个矩阵。

侧面注意:如果想花哨的话,可以使用itertools组合完成quad_row函数。优点是您可以输入任意大小的数组,并且它会为插值矩阵返回格式正确的行。该功能更灵活,但最终速度更慢。

from itertools import combinations
def interp_row(values):
    values = np.asarray(values)
    n = len(values)
    intp_row = [1]
    for i in range(1, n+1):
        intp_row.extend([np.product(x) for x in list(combinations(values, i))])
    return intp_row

如下所示,该函数可以接受输入表,查找接近插值的值,构建插值矩阵并执行矩阵数学运算。

def quad_interp(values, table):
    """
    values - four points to interpolate across, pass as list or numpy array
    table - lookup data, four input columns and one output column
    """
    table = np.asarray(table)
    A, B, C, D, E = np.transpose(table)
    a, b, c, d = values
    in_vector = quad_row(a, b, c, d)

    mask = (
        np.isin(A, findclose(a, A)) &
        np.isin(B, findclose(b, B)) &
        np.isin(C, findclose(c, C)) &
        np.isin(D, findclose(d, D)))

    quad_matrix = []
    c_vector = []

    for row in table[mask]:

        x, y, z, v, w = row
        quad_matrix.append(quad_row(x, y, z, v))
        c_vector.append(w)

    quad_matrix = np.matrix(quad_matrix)
    c_vector = np.asarray(c_vector)

    a_vector = np.dot(np.linalg.inv(quad_matrix), c_vector)

    return float(np.dot(a_vector, in_vector))

例如,调用该函数将如下所示。

df = pd.DataFrame({'liq':[5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 7000, 7000, 7000, 7000, 7000, 7000, 7000, 7000],
                  'visc':[10, 10, 10, 10, 30, 30, 30, 30, 10, 10, 10, 10, 30, 30, 30, 30],
                  'wlr':[0.375, 0.375, 0.5, 0.5, 0.375, 0.375, 0.5, 0.5, 0.375, 0.375, 0.5, 0.5, 0.375, 0.375, 0.5, 0.5],
                  'gvf':[0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75, 0.625, 0.75],
                  'uncert':[0.0707, 0.0992, 0.0906, 0.1278, 0.0705, 0.0994, 0.091, 0.128, 0.0702, 0.0991, 0.0905, 0.1279, 0.0704, 0.0992, 0.0904, 0.1283],
                  })

values = [6532, 22, 0.412, 0.634]

quad_interp(values, df)

可见,以上功能不存在错误处理。如果尝试以下操作,它将崩溃:
1。在表格边界之外插值。
2。输入表中已经存在的查找值,导致选择的点数少于16。

我也承认以下几点:
1。命名约定可能会更好
2。创建遮罩功能的方法可能更快捷

函数 findclose()显示为原始问题。

如果您有任何反馈或需要改进的地方,请告诉我