Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 通过多列对数据帧中的连续项进行群集/分组 问题:_Python_Pandas_Numpy_Scipy - Fatal编程技术网

Python 通过多列对数据帧中的连续项进行群集/分组 问题:

Python 通过多列对数据帧中的连续项进行群集/分组 问题:,python,pandas,numpy,scipy,Python,Pandas,Numpy,Scipy,假设我有k个标量列,并且我想将条目分组,如果它们沿着每列彼此之间的距离在一定范围内 假设simpicity k为2,它们是我唯一的列 pd.DataFrame(list(zip(sorted(choices(range(0,10), k=20)), choices(range(20,29), k=20))), columns=['a','b']) 屈服 [(1, 27), (1, 27), (1, 21), (2, 23), (3, 25), (4, 23), (4, 28),

假设我有k个标量列,并且我想将条目分组,如果它们沿着每列彼此之间的距离在一定范围内

假设simpicity k为2,它们是我唯一的列

pd.DataFrame(list(zip(sorted(choices(range(0,10), k=20)), choices(range(20,29), k=20))), columns=['a','b'])
屈服

[(1, 27),
 (1, 27),
 (1, 21),
 (2, 23),
 (3, 25),
 (4, 23),
 (4, 28),
 (4, 27),
 (4, 22),
 (4, 24),
 (5, 26),
 (6, 21),
 (7, 26),
 (7, 20),
 (8, 24),
 (8, 25),
 (8, 23),
 (9, 20),
 (9, 28),
 (9, 21)]
我需要分组,以便分组包括
a列中最多相隔
m
的条目,以及最多相隔
n
b列中的条目。如果
m
=
n
=1,则集群将为:

(1, 27), (1, 27)
(1, 21)
(2, 23)
(3, 25), (4, 23), (4, 22), (4, 24)
(4, 28), (4, 27), (5, 26)
(6, 21), (7, 20)
(7, 26), (8, 24), (8, 25), (8, 23)
(9, 20), (9, 21)
(9, 28),
笔记 实现这一点的一种方法是使用,但这不是一个好的解决方案,因为:

  • 我有很多数据,不想做平方运算
  • 数据已经排序,m,n相对于列的范围较小
  • m=/=n(不总是),否则m+n的曼哈顿距离阈值将起作用
我相信这可能是一个非常相关的问题,但它没有一个普遍的答案:

一种可能帮助您找到答案的方法的草图:

a, b, c, d, e = tee(range(10), 5)
next(b, None)
next(c, None);next(c, None)
next(d, None);next(d, None);next(d, None)
next(e, None);next(e, None);next(e, None);next(e, None)
list(zip(a, b, c, d, e))

[(0, 1, 2, 3, 4),
 (1, 2, 3, 4, 5),
 (2, 3, 4, 5, 6),
 (3, 4, 5, 6, 7),
 (4, 5, 6, 7, 8),
 (5, 6, 7, 8, 9)]

你的问题让我想起了
lag
操作和
cumsum
。这里有一个答案。如果您的数据很大,我认为使用python
list
tuple
就可以了,默认模块必须有函数来完成我们的任务

步骤1:获取数据 第2步:滞后1个长度 输出:

步骤3:定义custum函数,然后将观察结果分组
首先,我们使用
metric='chebyshev'

test = np.array([(1, 27),
 (1, 27),
 (1, 21),
 (2, 23),
 (3, 25),
 (4, 23),
 (4, 28),
 (4, 27),
 (4, 22),
 (4, 24),
 (5, 26),
 (6, 21),
 (7, 26),
 (7, 20),
 (8, 24),
 (8, 25),
 (8, 23),
 (9, 20),
 (9, 28),
 (9, 21)])

from scipy.spatial.distance import pdist, squareform
c_mat = squareform(pdist(test, metric = 'chebyshev')) <= 1
现在
c_mat
True
如果有任何链连接两行,那么
out
是所有单独组的布尔索引。现在返回结果:

for mask in list(out):
    print(np.unique(test[mask], axis = 0))

[[ 9 28]]
[[ 9 20]
 [ 9 21]]
[[ 7 26]
 [ 8 23]
 [ 8 24]
 [ 8 25]]
[[ 6 21]
 [ 7 20]]
[[ 4 27]
 [ 4 28]
 [ 5 26]]
[[ 3 25]
 [ 4 22]
 [ 4 23]
 [ 4 24]]
[[ 2 23]]
[[ 1 21]]
[[ 1 27]]
您还可以使用这些布尔索引访问原始
DataFrame

编辑1:

现在,我们可以利用输入是半排序的这一事实来加快速度。但要做到这一点,我们需要
numba

from numba import jit

@jit
def find_connected(data, dist = 1):
    i = list(range(data.shape[0]))
    j = list(range(data.shape[0]))
    l = data.shape[0]
    for x in range(1, l):
        for y in range(x, l):
            v = np.abs(data[x] - data[y])
            if v.max() <= dist:
                i += [x, y]
                j += [y, x]
            if v.min() > dist:
                break
    d = [1] * len(i)
    return (d, (i, j))
csr
做dot产品的速度要快得多,因此
c_mat=c_mat@c_mat
可以工作,但停止标准被打破。您可以使用Anreas K.的优秀答案,也可以只做
out=np.unique(c_mat.todense(),axis=0)

编辑2:

在我解决这个问题之前,我无法从脑海中摆脱出来,除非我没有制作一个稠密的矩阵

import numba as nb
import numpy as np
@nb.njit
def find_connected_semisort(data, dist = 1):
    l = data.shape[0]
    out = []
    for x in range(l):
        for y in range(x, l):
            v = np.abs(data[x] - data[y])
            if v.max() <= dist:
                out.append(set([x, y]))
            if v.min() > dist:
                break
    outlen = len(out)
    for x in range(outlen):
        for y in range(x + 1, outlen):
            if len(out[x] & out[y]) > 0:
                out[y] |= out[x]
                out[x].clear()
    return [list(i) for i in out if len(i) > 0]

[np.unique(test[i], axis = 0).squeeze() for i in find_connected_semisort(test)]
Out[]: 
[array([ 1, 27]), array([ 1, 21]), array([ 2, 23]), array([[ 3, 25],
    [ 4, 22],
    [ 4, 23],
    [ 4, 24]]), array([[ 4, 27],
    [ 4, 28],
    [ 5, 26]]), array([[ 6, 21],
    [ 7, 20]]), array([[ 7, 26],
    [ 8, 23],
    [ 8, 24],
    [ 8, 25]]), array([ 9, 28]), array([[ 9, 20],
    [ 9, 21]])]
将numba作为nb导入
将numpy作为np导入
@nb.njit
def find_connected_semisort(数据,dist=1):
l=数据。形状[0]
out=[]
对于范围(l)内的x:
对于范围(x,l)内的y:
v=np.abs(数据[x]-数据[y])
如果v.max()距离:
打破
outlen=len(out)
对于范围内的x(outlen):
对于范围内的y(x+1,outlen):
如果len(out[x]&out[y])>0:
out[y]|=out[x]
out[x].clear()
return[如果len(i)>0,则i in out的列表(i)]
[np.unique(test[i],axis=0).find\u connected\u semisort(test)中i的挤压()
出[]:
[array([1,27])、array([1,21])、array([2,23])、array([3,25]),以及,
[ 4, 22],
[ 4, 23],
[4,24]),数组([4,27],
[ 4, 28],
[5,26]),数组([6,21],
[7,20]),数组([7,26],
[ 8, 23],
[ 8, 24],
[8,25]),数组([9,28]),数组([9,20],
[ 9, 21]])]

可能有一些方法可以不用两个循环就完成,但我无法尝试。

在您的输出中,在不同的输出列表中有(6,28)的两个副本?当我们生成数据时,可能在不同的位置有相同的值。我没有弄清楚,在你的帖子中,观察的顺序是否应该与你的例子中的
(3,25)
(4,23)、(4,22)、(4,24)
放在一起?@AndreasK。我对此也感到困惑。看来观察的顺序很重要。例如,假设
(3,25)
下一个
样本是
(4,23)
,因为
4-3=1,23-25=-2
所以(4,23)与(3,25)分开。但这并不能解释为什么(3,25)和(4,24)不在同一组中……
pdist
不需要使用平方运算。使用
metric='cityblock'
(曼哈顿距离)你将非常接近你想要的。@AndreasK你是对的,对此表示抱歉,我已经修复了这个示例。@DanielF你能给我指一些文献吗pdist如何使用cityblock获得加速?很好的解决方案+1.但是,如果m=/=n怎么办?我想你可以做一些像
c_mat=squareform((pdist(test[:,0][:,None],'cityblock'))
1     [(1, 26), (1, 27), (1, 28)]
2                       [(2, 22)]
3                       [(2, 24)]
4                       [(3, 20)]
5                       [(3, 26)]
6              [(4, 21), (4, 20)]
7                       [(5, 28)]
8              [(5, 26), (5, 26)]
9                       [(6, 28)]
10                      [(6, 24)]
11                      [(6, 28)]
12                      [(7, 23)]
13                      [(7, 26)]
14             [(8, 28), (8, 28)]
15                      [(9, 26)]
dtype: object
test = np.array([(1, 27),
 (1, 27),
 (1, 21),
 (2, 23),
 (3, 25),
 (4, 23),
 (4, 28),
 (4, 27),
 (4, 22),
 (4, 24),
 (5, 26),
 (6, 21),
 (7, 26),
 (7, 20),
 (8, 24),
 (8, 25),
 (8, 23),
 (9, 20),
 (9, 28),
 (9, 21)])

from scipy.spatial.distance import pdist, squareform
c_mat = squareform(pdist(test, metric = 'chebyshev')) <= 1
out = np.ones((c_mat.shape[0], 2))
while out.sum(0).max() >1:
    c_mat = c_mat @ c_mat
    out = np.unique(c_mat, axis = 0)
for mask in list(out):
    print(np.unique(test[mask], axis = 0))

[[ 9 28]]
[[ 9 20]
 [ 9 21]]
[[ 7 26]
 [ 8 23]
 [ 8 24]
 [ 8 25]]
[[ 6 21]
 [ 7 20]]
[[ 4 27]
 [ 4 28]
 [ 5 26]]
[[ 3 25]
 [ 4 22]
 [ 4 23]
 [ 4 24]]
[[ 2 23]]
[[ 1 21]]
[[ 1 27]]
from numba import jit

@jit
def find_connected(data, dist = 1):
    i = list(range(data.shape[0]))
    j = list(range(data.shape[0]))
    l = data.shape[0]
    for x in range(1, l):
        for y in range(x, l):
            v = np.abs(data[x] - data[y])
            if v.max() <= dist:
                i += [x, y]
                j += [y, x]
            if v.min() > dist:
                break
    d = [1] * len(i)
    return (d, (i, j))
from scipy.sparse import csr_matrix

c_mat =  csr_matrix(find_connected(test), dtype = bool)
import numba as nb
import numpy as np
@nb.njit
def find_connected_semisort(data, dist = 1):
    l = data.shape[0]
    out = []
    for x in range(l):
        for y in range(x, l):
            v = np.abs(data[x] - data[y])
            if v.max() <= dist:
                out.append(set([x, y]))
            if v.min() > dist:
                break
    outlen = len(out)
    for x in range(outlen):
        for y in range(x + 1, outlen):
            if len(out[x] & out[y]) > 0:
                out[y] |= out[x]
                out[x].clear()
    return [list(i) for i in out if len(i) > 0]

[np.unique(test[i], axis = 0).squeeze() for i in find_connected_semisort(test)]
Out[]: 
[array([ 1, 27]), array([ 1, 21]), array([ 2, 23]), array([[ 3, 25],
    [ 4, 22],
    [ 4, 23],
    [ 4, 24]]), array([[ 4, 27],
    [ 4, 28],
    [ 5, 26]]), array([[ 6, 21],
    [ 7, 20]]), array([[ 7, 26],
    [ 8, 23],
    [ 8, 24],
    [ 8, 25]]), array([ 9, 28]), array([[ 9, 20],
    [ 9, 21]])]