我想根据采样数据构建网格。我可以使用机器学习 - 聚类算法,如k-means,但我想限制中心大致均匀分布。
我想出了一种使用scikit-learn最近邻搜索的方法:随机选择一个点,删除半径为r的所有点然后重复。这很好用,但想知道是否有人有更好(更快)的方法。
作为对评论的回应,我尝试了两种替代方法,一种方法变得慢得多,另一种方法差不多......
方法0(我的第一次尝试):
def get_centers0(X, r):
N = X.shape[0]
D = X.shape[1]
grid = np.zeros([0,D])
nearest = near.NearestNeighbors(radius = r, algorithm = 'auto')
while N > 0:
nearest.fit(X)
x = X[int(random()*N), :]
_, del_x = nearest.radius_neighbors(x)
X = np.delete(X, del_x[0], axis = 0)
grid = np.vstack([grid, x])
N = X.shape[0]
return grid
方法1(使用预先计算的图表):
def get_centers1(X, r):
N = X.shape[0]
D = X.shape[1]
grid = np.zeros([0,D])
nearest = near.NearestNeighbors(radius = r, algorithm = 'auto')
nearest.fit(X)
graph = nearest.radius_neighbors_graph(X)
#This method is very slow even before doing any 'pruning'
方法2:
def get_centers2(X, r, k):
N = X.shape[0]
D = X.shape[1]
k = k
grid = np.zeros([0,D])
nearest = near.NearestNeighbors(radius = r, algorithm = 'auto')
while N > 0:
nearest.fit(X)
x = X[np.random.randint(0,N,k), :]
#min_dist = near.NearestNeighbors().fit(x).kneighbors(x, n_neighbors = 1, return_distance = True)
min_dist = dist(x, k, 2, np.ones(k)) # where dist is a cython compiled function
x = x[min_dist < 0.1,:]
_, del_x = nearest.radius_neighbors(x)
X = np.delete(X, del_x[0], axis = 0)
grid = np.vstack([grid, x])
N = X.shape[0]
return grid
按如下方式运行:
N = 50000
r = 0.1
x1 = np.random.rand(N)
x2 = np.random.rand(N)
X = np.vstack([x1, x2]).T
tic = time.time()
grid0 = get_centers0(X, r)
toc = time.time()
print 'Method 0: ' + str(toc - tic)
tic = time.time()
get_centers1(X, r)
toc = time.time()
print 'Method 1: ' + str(toc - tic)
tic = time.time()
grid2 = get_centers2(X, r)
toc = time.time()
print 'Method 1: ' + str(toc - tic)
方法0和2大致相同......
Method 0: 0.840130090714
Method 1: 2.23365592957
Method 2: 0.774812936783
答案 0 :(得分:4)
我不确定你究竟想要做什么。您提到想要创建“近似网格”或“均匀分布”,而您提供的代码会选择一个点子集,使得成对距离不会大于r
。
一些可能的建议:
如果您想要的是近似网格,我会构建您想要近似的网格,然后查询每个网格点的最近邻居。根据您的应用,您可能会进一步将这些结果修剪为与网格点的距离大于对您有用的切割点。
如果你想要的是从这些点中抽取的近似均匀的分布,我会在每个点做一个核密度估计(sklearn.neighbors.KernelDensity
),并做一个随机的从每个点的局部密度的倒数加权的数据集中进行子选择。
如果您想要的是点的子集,使得成对距离不会大于 r
,我首先要构建一个半径radius_neighbors_graph
{ {1}},它将一次性为您提供一个太靠近的所有点的列表。然后,您可以使用类似于上面编写的修剪算法,根据这些稀疏图形距离删除点。
我希望有所帮助!
答案 1 :(得分:4)
我提出了一种非常简单的方法,它比我以前的尝试更有效率。
这个只是循环遍历数据集,并且只有当它大于距离所有现有中心的r距离时,才将当前点添加到网格点列表中。这种方法比我以前的尝试快了大约20倍。因为没有涉及外部库我可以在cython中运行这一切...
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def get_centers_fast(np.ndarray[DTYPE_t, ndim = 2] x, double radius):
cdef int N = x.shape[0]
cdef int D = x.shape[1]
cdef int m = 1
cdef np.ndarray[DTYPE_t, ndim = 2] xc = np.zeros([10000, D])
cdef double r = 0
cdef double r_min = 10
cdef int i, j, k
for k in range(D):
xc[0,k] = x[0,k]
for i in range(1, N):
r_min = 10
for j in range(m):
r = 0
for k in range(D):
r += (x[i, k] - xc[j, k])**2
r = r**0.5
if r < r_min:
r_min = r
if r_min > radius:
m = m + 1
for k in range(D):
xc[m - 1,k] = x[i,k]
nonzero = np.nonzero(xc[:,0])[0]
xc = xc[nonzero,:]
return xc
按如下方式运行这些方法:
N = 40000
r = 0.1
x1 = np.random.normal(size = N)
x1 = (x1 - min(x1)) / (max(x1)-min(x1))
x2 = np.random.normal(size = N)
x2 = (x2 - min(x2)) / (max(x2)-min(x2))
X = np.vstack([x1, x2]).T
tic = time.time()
grid0 = gt.get_centers0(X, r)
toc = time.time()
print 'Method 0: ' + str(toc - tic)
tic = time.time()
grid2 = gt.get_centers2(X, r, 10)
toc = time.time()
print 'Method 2: ' + str(toc - tic)
tic = time.time()
grid3 = gt.get_centers_fast(X, r)
toc = time.time()
print 'Method 3: ' + str(toc - tic)
新方法快了大约20倍。如果我提前停止循环(例如,如果连续的k次迭代无法产生新的中心),它可以更快地完成。
Method 0: 0.219595909119
Method 2: 0.191949129105
Method 3: 0.0127329826355
答案 2 :(得分:1)
也许你只能每隔k&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; N个删除加速了这个过程。大多数时候,社区结构不应该有太大变化。
答案 3 :(得分:0)
听起来你正在尝试重新发明以下其中一项:
即。这个概念已经发明了至少三次,但变化很小。
从技术上讲,没有聚类。 K-means也没有真正聚集。
更充分地描述为矢量量化。