簡體   English   中英

如何在二維 numpy 數組上實現二維高斯分布

[英]How to implement a 2D Gaussian on a 2D numpy array

我有一個大小為 10 x 10 的二維 NumPy 數組,我試圖在其中實現二維高斯分布,以便我可以將新列用作我的 ML model 中的一個特征。中心(高斯分布的峰值) 應該位於二維 NumPy 數組的 (3, 5) 處。 Python有什么辦法嗎? 我還包含了我的 np 數組的熱圖。 我感謝任何反饋! 新年快樂!

這是我的數據:

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.stats import multivariate_normal
    my_np_list = [310.90634 , 287.137   , 271.87973 , 266.6     , 271.87973 ,
           287.137   , 310.90634 , 341.41458 , 377.02936 , 416.44254 ,
           266.6     , 238.4543  , 219.844   , 213.28001 , 219.844   ,
           238.4543  , 266.6     , 301.62347 , 341.41458 , 384.496   ,
           226.2176  , 192.248   , 168.61266 , 159.96    , 168.61266 ,
           192.248   , 226.2176  , 266.6     , 310.90634 , 357.68146 ,
           192.248   , 150.81174 , 119.22715 , 106.64001 , 119.22715 ,
           150.81174 , 192.248   , 238.4543  , 287.137   , 337.2253  ,
           168.61266 , 119.22715 ,  75.40587 ,  53.320004,  75.40587 ,
           119.22715 , 168.61266 , 219.844   , 271.87973 , 324.33292 ,
           159.96    , 106.64001 ,  53.320004,   0.      ,  53.320004,
           106.64001 , 159.96    , 213.28001 , 266.6     , 319.92    ,
           168.61266 , 119.22715 ,  75.40587 ,  53.320004,  75.40587 ,
           119.22715 , 168.61266 , 219.844   , 271.87973 , 324.33292 ,
           192.248   , 150.81174 , 119.22715 , 106.64001 , 119.22715 ,
           150.81174 , 192.248   , 238.4543  , 287.137   , 337.2253  ,
           226.2176  , 192.248   , 168.61266 , 159.96    , 168.61266 ,
           192.248   , 226.2176  , 266.6     , 310.90634 , 357.68146 ,
           266.6     , 238.4543  , 219.844   , 213.28001 , 219.844   ,
           238.4543  , 266.6     , 301.62347 , 341.41458 , 384.496   ]
    
    my_np_array = np.array(my_np_list).reshape(10, 10)
    plt.imshow(my_np_array, interpolation='none')
    plt.show()
    
    
    size = 100
    store_center = (3, 5)
    i_center = 3
    j_center = 5

在此處輸入圖像描述

我在我的陣列上嘗試了 scipy.stats.multivariate_normal.pdf,但它沒有用。

    import scipy
    from scipy import stats
    
    my_np_array = my_np_array.reshape(-1)
    y = scipy.stats.multivariate_normal.pdf(my_np_array, mean=2, cov=0.5)
    
    y = y.reshape(10,10)
    
    yy = np.dot(y.T,y)

如果我正確理解你的問題,你想計算一個二維高斯 PDF 與你在my_np_array中的數據具有相同的形狀(例如 10 x 10)。 您當前的代碼僅在將其重塑為一維時計算一維分布,並且僅包括一個均值和 cov。 相反,嘗試創建一個索引值網格(所有 i、j 對)並分別使用i_centerj_center作為沿ij維度分布的兩個平均值,如下所示:

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal

my_np_list = [310.90634 , 287.137   , 271.87973 , 266.6     , 271.87973 ,
       287.137   , 310.90634 , 341.41458 , 377.02936 , 416.44254 ,
       266.6     , 238.4543  , 219.844   , 213.28001 , 219.844   ,
       238.4543  , 266.6     , 301.62347 , 341.41458 , 384.496   ,
       226.2176  , 192.248   , 168.61266 , 159.96    , 168.61266 ,
       192.248   , 226.2176  , 266.6     , 310.90634 , 357.68146 ,
       192.248   , 150.81174 , 119.22715 , 106.64001 , 119.22715 ,
       150.81174 , 192.248   , 238.4543  , 287.137   , 337.2253  ,
       168.61266 , 119.22715 ,  75.40587 ,  53.320004,  75.40587 ,
       119.22715 , 168.61266 , 219.844   , 271.87973 , 324.33292 ,
       159.96    , 106.64001 ,  53.320004,   0.      ,  53.320004,
       106.64001 , 159.96    , 213.28001 , 266.6     , 319.92    ,
       168.61266 , 119.22715 ,  75.40587 ,  53.320004,  75.40587 ,
       119.22715 , 168.61266 , 219.844   , 271.87973 , 324.33292 ,
       192.248   , 150.81174 , 119.22715 , 106.64001 , 119.22715 ,
       150.81174 , 192.248   , 238.4543  , 287.137   , 337.2253  ,
       226.2176  , 192.248   , 168.61266 , 159.96    , 168.61266 ,
       192.248   , 226.2176  , 266.6     , 310.90634 , 357.68146 ,
       266.6     , 238.4543  , 219.844   , 213.28001 , 219.844   ,
       238.4543  , 266.6     , 301.62347 , 341.41458 , 384.496   ]

my_np_array = np.array(my_np_list).reshape(10, 10)
plt.imshow(my_np_array, interpolation='none')
plt.show()

i_center = 3
j_center = 5

# Create grid of i, j points
# See https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.multivariate_normal.html
j_n, i_n = my_np_array.shape
j_grid, i_grid = np.mgrid[0:j_n, 0:i_n]
pos = np.dstack((j_grid, i_grid))

# Create 2D Gaussian PDF values for grid
rv = multivariate_normal([j_center, i_center], [[0.5, 0], [0, 0.5]])
y = rv.pdf(pos)

# Plot 2D PDF values
plt.imshow(y, interpolation='none')
plt.show()

二維高斯 PDF 輸出

pdf 在pos中的所有 j 和 i 對上進行評估, j_centeri_center值提供 position,其中出現二維高斯分布中的峰值(即平均值)。 cov矩陣只有對角線填充了您提供的值,因為它看起來像您希望基於數據的對稱,但您可以使用非對角線值來查看會發生什么。

這是最適合的 2-Gaussian。

import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize

my_np_list = [
    310.90634 , 287.137   , 271.87973 , 266.6     , 271.87973 ,
    287.137   , 310.90634 , 341.41458 , 377.02936 , 416.44254 ,
    266.6     , 238.4543  , 219.844   , 213.28001 , 219.844   ,
    238.4543  , 266.6     , 301.62347 , 341.41458 , 384.496   ,
    226.2176  , 192.248   , 168.61266 , 159.96    , 168.61266 ,
    192.248   , 226.2176  , 266.6     , 310.90634 , 357.68146 ,
    192.248   , 150.81174 , 119.22715 , 106.64001 , 119.22715 ,
    150.81174 , 192.248   , 238.4543  , 287.137   , 337.2253  ,
    168.61266 , 119.22715 ,  75.40587 ,  53.320004,  75.40587 ,
    119.22715 , 168.61266 , 219.844   , 271.87973 , 324.33292 ,
    159.96    , 106.64001 ,  53.320004,   0.      ,  53.320004,
    106.64001 , 159.96    , 213.28001 , 266.6     , 319.92    ,
    168.61266 , 119.22715 ,  75.40587 ,  53.320004,  75.40587 ,
    119.22715 , 168.61266 , 219.844   , 271.87973 , 324.33292 ,
    192.248   , 150.81174 , 119.22715 , 106.64001 , 119.22715 ,
    150.81174 , 192.248   , 238.4543  , 287.137   , 337.2253  ,
    226.2176  , 192.248   , 168.61266 , 159.96    , 168.61266 ,
    192.248   , 226.2176  , 266.6     , 310.90634 , 357.68146 ,
    266.6     , 238.4543  , 219.844   , 213.28001 , 219.844   ,
    238.4543  , 266.6     , 301.62347 , 341.41458 , 384.496   ,
]

my_np_array = np.array(my_np_list).reshape(10, -1)


def gaussian2(xy: np.ndarray, a: float, b: float, c: float, d: float, e: float, f: float) -> np.ndarray:
    x, y = xy

    z = (
        a - b
        * np.exp(-((x - c)/d)**2)
        * np.exp(-((y - e)/f)**2)
    )
    return z


xy = np.stack(
    np.meshgrid(
        np.arange(my_np_array.shape[1]),
        np.arange(my_np_array.shape[0]),
    )
).reshape((2, -1))

param, _ = scipy.optimize.curve_fit(
    f=gaussian2,
    xdata=xy,
    ydata=my_np_array.ravel(),
    p0=(400, 400,
        3, 1,
        5, 1)
)
print(param)
zfit = gaussian2(xy, *param).reshape(my_np_array.shape)

fig, (ax_act, ax_fit) = plt.subplots(nrows=1, ncols=2)
ax_act.imshow(my_np_array)
ax_fit.imshow(zfit)
plt.show()
[447.47305265 394.42329346   3.02857599   5.53214092   4.98984104
   5.56048623]

最適合

它太寬泛了,所以如果你想要更好的擬合,你需要使用非高斯的東西。 例如,大約 1.7 和 1.8 的修改后的指數提供了一個很好的擬合 - 打折你的峰值“0”,這在我看來是假的。

def gaussian2(xy: np.ndarray, a: float, b: float, c: float, d: float, e: float, f: float, g: float, h: float) -> np.ndarray:
    x, y = xy

    z = (
        a - b
        * np.exp(-np.abs((x - c)/d)**e)
        * np.exp(-np.abs((y - f)/g)**h)
    )
    return z


param, _ = scipy.optimize.curve_fit(
    f=gaussian2,
    xdata=xy,
    ydata=my_np_array.ravel(),
    p0=(400, 400,
        3, 5, 2,
        5, 5, 2)
)
[482.96976151 441.22504655   3.01091214   6.11061124   1.79338408
   5.00625763   6.27235212   1.69061652]

非高斯擬合

如果您從擬合中排除假峰,這將進一步改善:

z_flat = my_np_array.ravel()
not_zero, = np.nonzero(z_flat)
z_flat = z_flat[not_zero]
xy = xy[:, not_zero]
# ...

zfit = np.zeros_like(my_np_array)
x, y = xy
zfit[y, x] = gaussian2(xy, *param)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM