具有自定义指标的sklearn聚类:pairwise_distances抛出错误

时间:2019-07-06 11:02:05

标签: python pandas numpy scikit-learn

我想使用自己的指标对空间数据集进行聚类。数据以数据帧中(x,y)对的形式出现,其中每对对都有一个id。就像下面的示例中,我有三组点:

import pandas as pd 
import numpy as np

df = pd.DataFrame({'id': [1] * 4 + [2] * 5 + [3] * 3, 
                   'x': np.random.random(12),
                   'y': np.random.random(12)}) 
df['xy'] = df[['x','y']].apply(lambda row: [row['x'],row['y']], axis = 1)

这是我要使用的距离函数:

from scipy.spatial.distance import directed_hausdorff
def some_distance(u, v):
    return max(directed_hausdorff(u, v)[0], directed_hausdorff(v, u)[0])

此函数计算Hausdorff distance,即u维空间的两个子集vn之间的距离。就我而言,我想使用该距离函数对真实平面的子集进行聚类。在上面的数据中,存在三个此类子集(从1到3的id),因此,得出的距离矩阵应为3x3。

我对聚类步骤的想法是将sklearn.cluster.AgglomerativeClustering与预先计算的指标一起使用,而我又要使用sklearn.metrics.pairwise import pairwise_distances进行计算。

from sklearn.metrics.pairwise import pairwise_distances
def to_np_array(col):
    return np.array(list(col.values))
X = df.groupby('id')['xy'].apply(to_np_array).as_matrix()
m = pairwise_distances(X, X, metric=some_distance)

但是,最后一行给我一个错误:

ValueError: setting an array element with a sequence.

有效的方法是调用some_distance(X[1], X[2])。 我的直觉是,X必须采用不同的格式才能工作pairwise_distances。关于如何进行这项工作或如何自己计算矩阵的任何想法,以便我可以将其放入sklearn.cluster.AgglomerativeClustering中?

错误堆栈为

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-3-e34155622595> in <module>
     12 def some_distance(u, v):
     13     return max(directed_hausdorff(u, v)[0], directed_hausdorff(v, u)[0])
---> 14 m = pairwise_distances(X, X, metric=some_distance)

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\metrics\pairwise.py in pairwise_distances(X, Y, metric, n_jobs, **kwds)
   1430         func = partial(distance.cdist, metric=metric, **kwds)
   1431 
-> 1432     return _parallel_pairwise(X, Y, func, n_jobs, **kwds)
   1433 
   1434 

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\metrics\pairwise.py in _parallel_pairwise(X, Y, func, n_jobs, **kwds)
   1065 
   1066     if effective_n_jobs(n_jobs) == 1:
-> 1067         return func(X, Y, **kwds)
   1068 
   1069     # TODO: in some cases, backend='threading' may be appropriate

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\metrics\pairwise.py in _pairwise_callable(X, Y, metric, **kwds)
   1079     """Handle the callable case for pairwise_{distances,kernels}
   1080     """
-> 1081     X, Y = check_pairwise_arrays(X, Y)
   1082 
   1083     if X is Y:

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\metrics\pairwise.py in check_pairwise_arrays(X, Y, precomputed, dtype)
    106     if Y is X or Y is None:
    107         X = Y = check_array(X, accept_sparse='csr', dtype=dtype,
--> 108                             warn_on_dtype=warn_on_dtype, estimator=estimator)
    109     else:
    110         X = check_array(X, accept_sparse='csr', dtype=dtype,

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\utils\validation.py in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, warn_on_dtype, estimator)
    525             try:
    526                 warnings.simplefilter('error', ComplexWarning)
--> 527                 array = np.asarray(array, dtype=dtype, order=order)
    528             except ComplexWarning:
    529                 raise ValueError("Complex data not supported\n"

C:\ProgramData\Anaconda3\lib\site-packages\numpy\core\numeric.py in asarray(a, dtype, order)
    536 
    537     """
--> 538     return array(a, dtype, copy=False, order=order)
    539 
    540 

ValueError: setting an array element with a sequence.

2 个答案:

答案 0 :(得分:0)

尝试一下。...

import numpy as np
import pandas as pd
from scipy.spatial.distance import directed_hausdorff
from sklearn.metrics.pairwise import pairwise_distances
from sklearn.cluster import AgglomerativeClustering

df = pd.DataFrame({'id': [1] * 4 + [2] * 5 + [3] * 3, 'x':
np.random.random(12), 'y': np.random.random(12)}) 
df['xy'] = df[['x','y']].apply(lambda row: [row['x'],row['y']], axis = 1)
df.groupby('id')['xy'].apply(to_np_array)


def some_distance(u, v):
    return max(directed_hausdorff(u, v)[0], directed_hausdorff(v, u)[0])


def to_np_array(col):
    return np.array(list(col.values))


X = df.groupby('id')['xy'].apply(to_np_array)
d = np.zeros((len(X),len(X)))

for i, u in enumerate(X):
    for j, v in list(enumerate(X))[i:]:
        d[i,j] = some_distance(u,v)
        d[j,i] = d[i,j]

现在,当您打印d时,您会收到此信息。

array([[0.        , 0.58928274, 0.40767213],
   [0.58928274, 0.        , 0.510095  ],
   [0.40767213, 0.510095  , 0.        ]])

用于群集...

cluster = AgglomerativeClustering(n_clusters=2, affinity='precomputed', linkage = 'average')
cluster.fit(d)

希望这会有所帮助!

答案 1 :(得分:0)

如果您显示一些变量,它将有所帮助。幸运的是,您提供了足够的代码来运行它。例如数据框:

In [9]: df                                                                                                      
Out[9]: 
    id         x         y                                          xy
0    1  0.428437  0.267264   [0.42843730501201727, 0.2672637429997736]
1    1  0.944687  0.023323  [0.9446872371859233, 0.023322969159167317]
2    1  0.091055  0.683154   [0.09105472832178496, 0.6831542985617349]
3    1  0.474522  0.313541    [0.4745222021519122, 0.3135405569298565]
4    2  0.835237  0.491541    [0.8352366339973815, 0.4915408434083248]
5    2  0.905918  0.854030    [0.9059178939221513, 0.8540297797160584]
6    2  0.182154  0.909656   [0.18215390836391654, 0.9096555360282939]
7    2  0.225270  0.522193   [0.22527013482912195, 0.5221926076838651]
8    2  0.924208  0.858627    [0.9242076604008371, 0.8586274362498842]
9    3  0.419813  0.634741   [0.41981292371175905, 0.6347409684931891]
10   3  0.954141  0.795452    [0.9541413559045294, 0.7954524369652217]
11   3  0.896593  0.271187    [0.8965932351250882, 0.2711872631673109]

还有您的X

In [10]: X                                                                                                      
Out[10]: 
array([array([[0.42843731, 0.26726374],
       [0.94468724, 0.02332297],
       [0.09105473, 0.6831543 ],
       [0.4745222 , 0.31354056]]),
       array([[0.83523663, 0.49154084],
       [0.90591789, 0.85402978],
       [0.18215391, 0.90965554],
       [0.22527013, 0.52219261],
       [0.92420766, 0.85862744]]),
       array([[0.41981292, 0.63474097],
       [0.95414136, 0.79545244],
       [0.89659324, 0.27118726]])], dtype=object)

这是一个(3,)对象数组-实际上是3个2d数组的列表,具有不同的大小((3,2),(5,2),(4,2))。那是每个组的一个数组。

pairwise应该如何将其提供给您的距离代码? pairwise文档说X应该是(n,m)数组-n个样本,m个特征。您的X不符合该描述!

尝试通过X制作浮点数组时可能会产生错误:

In [12]: np.asarray(X,dtype=float)                                                                              
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-12-a6e08bb1590c> in <module>
----> 1 np.asarray(X,dtype=float)

/usr/local/lib/python3.6/dist-packages/numpy/core/numeric.py in asarray(a, dtype, order)
    536 
    537     """
--> 538     return array(a, dtype, copy=False, order=order)
    539 
    540 

ValueError: setting an array element with a sequence.