将散点图分配到特定的容器中

2024-06-16 14:40:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个scatter plot,它被排序为4 Bins。它们由中间的两个arcs和一个line隔开(见下图)。在

这两个arcs有一个小问题。如果X-Coordiante大于ang2,则不会将其归因于正确的Bin。(见下图)

import math
import matplotlib.pyplot as plt
import matplotlib as mpl

X = [24,15,71,72,6,13,77,52,52,62,46,43,31,35,41]  
Y = [94,61,76,83,69,86,78,57,45,94,82,74,56,70,94]      

fig, ax = plt.subplots()
ax.set_xlim(-100,100)
ax.set_ylim(-40,140)
ax.grid(False)

plt.scatter(X,Y)

#middle line
BIN_23_X = 0 
#two arcs
ang1 = -60, 60
ang2 = 60, 60
angle = math.degrees(math.acos(2/9.15))
E_xy = 0,60

Halfway = mpl.lines.Line2D((BIN_23_X,BIN_23_X), (0,125), color = 'white', lw = 1.5, alpha = 0.8, zorder = 1)
arc1 = mpl.patches.Arc(ang1, 70, 110, angle = 0, theta2 = angle, theta1 = 360-angle, color = 'white', lw = 2)
arc2 = mpl.patches.Arc(ang2, 70, 110, angle = 0, theta2 = 180+angle, theta1 = 180-angle, color = 'white', lw = 2)
Oval = mpl.patches.Ellipse(E_xy, 160, 130, lw = 3, edgecolor = 'black', color = 'white', alpha = 0.2)

ax.add_line(Halfway)
ax.add_patch(arc1)
ax.add_patch(arc2)
ax.add_patch(Oval)

#Sorting the coordinates into bins   
def get_nearest_arc_vert(x, y, arc_vertices):
err = (arc_vertices[:,0] - x)**2 + (arc_vertices[:,1] - y)**2
nearest = (arc_vertices[err == min(err)])[0]
return nearest

arc1v = ax.transData.inverted().transform(arc1.get_verts())
arc2v = ax.transData.inverted().transform(arc2.get_verts())

def classify_pointset(vx, vy):
    bins = {(k+1):[] for k in range(4)}
    for (x,y) in zip(vx, vy):
        nx1, ny1 = get_nearest_arc_vert(x, y, arc1v)
        nx2, ny2 = get_nearest_arc_vert(x, y, arc2v)

        if x < nx1:                         
            bins[1].append((x,y))
        elif x > nx2:                      
            bins[4].append((x,y))
        else:
            if x < BIN_23_X:               
                bins[2].append((x,y))
            else:                          
               bins[3].append((x,y))
    return bins

#Bins Output
bins_red  = classify_pointset(X,Y)

all_points = [None] * 5
for bin_key in [1,2,3,4]:
    all_points[bin_key] = bins_red[bin_key] 

输出:

^{pr2}$

这不太正确。看看下面的figure output4 coordinatesBin 3中,11在{}。但是8属于Bin 3,而{}则归属于{}。在

我认为问题是blue coordinates。具体地说,当X-Coordinate大于ang2,即60。如果我将这些更改为小于60,它们将被更正为Bin 3。在

我不确定是否应该将arcs扩展到大于60,或者代码是否可以改进?在

请注意,这只是Bin 4ang2。此问题将出现在Bin 1ang1中。也就是说,如果X-coorridate小于60,它就不会归属于Bin 1

预期输出:

[[], [], [(24, 94), (15, 61), (6, 69), (13, 86)], [(71, 76), (72, 83), (52, 57), (52, 45), (46, 82), (43, 74), (31, 56), (35, 70), (41, 94), (77, 78), (62, 94)]]

enter image description here

注:首选预期输出。该示例使用一个row的输入数据。但是,我的数据集要大得多。如果我们使用多个rows,那么输出应该是逐行的。e、 g

#Numerous rows
X = np.random.randint(50, size=(100, 10))
Y = np.random.randint(80, size=(100, 10)) 

输出:

Row 0 = [(x,y)],[(x,y)],[(x,y)],[(x,y)]
Row 1 = [(x,y)],[(x,y)],[(x,y)],[(x,y)]
Row 2 = [(x,y)],[(x,y)],[(x,y)],[(x,y)]
etc

Tags: addgetbinaxmplcolorwhitearcs
2条回答

补丁有一个测试是否包含点:contains_point,甚至包括点数组:contains_points

我有一个代码片段给你,你可以在添加补丁的部分和#Sorting the coordinates into bins代码块之间添加。在

它添加两个额外的(透明的)椭圆,用于计算如果圆弧是完全闭合的椭圆,这些圆弧是否包含点。如果一个点属于大椭圆,左或右省略号,或者具有正或负的x坐标,那么您的bin计算只是一个布尔测试组合。在

ov1 = mpl.patches.Ellipse(ang1, 70, 110, alpha=0)
ov2 = mpl.patches.Ellipse(ang2, 70, 110, alpha=0)
ax.add_patch(ov1)
ax.add_patch(ov2)

for px, py in zip(X, Y):
    in_oval = Oval.contains_point(ax.transData.transform(([px, py])), 0)
    in_left = ov1.contains_point(ax.transData.transform(([px, py])), 0)
    in_right = ov2.contains_point(ax.transData.transform(([px, py])), 0)
    on_left = px < 0
    on_right = px > 0
    if in_oval:
        if in_left:
            n_bin = 1
        elif in_right:
            n_bin = 4
        elif on_left:
            n_bin = 2
        elif on_right:
            n_bin = 3
        else:
            n_bin = -1
    else:
        n_bin = -1
    print('({:>2}/{:>2}) is {}'.format(px, py, 'in Bin ' +str(n_bin) if n_bin>0 else 'outside'))

输出为:

^{pr2}$

注意,当点的x-coord=0时,您仍然应该决定如何定义bin—此时它们等于outside,因为on_left和{}都不觉得对它们负责。。。在

PS:感谢@ImportanceOfBeingErnest提供了必要转换的提示:https://stackoverflow.com/a/49112347/8300135

注意:对于以下所有编辑,您需要import numpy as np
编辑: 计算每个X, Y数组输入的bin分布的函数:

def bin_counts(X, Y):
    bc = dict()
    E = Oval.contains_points(ax.transData.transform(np.array([X, Y]).T), 0)
    E_l = ov1.contains_points(ax.transData.transform(np.array([X, Y]).T), 0)
    E_r = ov2.contains_points(ax.transData.transform(np.array([X, Y]).T), 0)
    L = np.array(X) < 0
    R = np.array(X) > 0
    bc[1] = np.sum(E & E_l)
    bc[2] = np.sum(E & L & ~E_l)
    bc[3] = np.sum(E & R & ~E_r)
    bc[4] = np.sum(E & E_r)
    return bc

将导致以下结果:

bin_counts(X, Y)
Out: {1: 0, 2: 0, 3: 4, 4: 10}

编辑2: X和Y的两个二维数组中的许多行:

np.random.seed(42)
X = np.random.randint(-80, 80, size=(100, 10))
Y = np.random.randint(0, 120, size=(100, 10))

在所有行上循环:

for xr, yr in zip(X, Y):
    print(bin_counts(xr, yr))

结果:

{1: 1, 2: 2, 3: 6, 4: 0}
{1: 1, 2: 0, 3: 4, 4: 2}
{1: 5, 2: 2, 3: 1, 4: 1}
...
{1: 3, 2: 2, 3: 2, 4: 0}
{1: 2, 2: 4, 3: 1, 4: 1}
{1: 1, 2: 1, 3: 6, 4: 2}

编辑3: 若要返回的不是每个bin中的点数,而是包含四个数组的数组,其中包含每个bin中点的x、y坐标,请使用以下命令:

X = [24,15,71,72,6,13,77,52,52,62,46,43,31,35,41]  
Y = [94,61,76,83,69,86,78,57,45,94,82,74,56,70,94]      

def bin_points(X, Y):
    X = np.array(X)
    Y = np.array(Y)
    E = Oval.contains_points(ax.transData.transform(np.array([X, Y]).T), 0)
    E_l = ov1.contains_points(ax.transData.transform(np.array([X, Y]).T), 0)
    E_r = ov2.contains_points(ax.transData.transform(np.array([X, Y]).T), 0)
    L = X < 0
    R = X > 0
    bp1 = np.array([X[E & E_l], Y[E & E_l]]).T
    bp2 = np.array([X[E & L & ~E_l], Y[E & L & ~E_l]]).T
    bp3 = np.array([X[E & R & ~E_r], Y[E & R & ~E_r]]).T
    bp4 = np.array([X[E & E_r], Y[E & E_r]]).T
    return [bp1, bp2, bp3, bp4]

print(bin_points(X, Y))
[array([], shape=(0, 2), dtype=int32), array([], shape=(0, 2), dtype=int32), array([[24, 94],
       [15, 61],
       [ 6, 69],
       [13, 86]]), array([[71, 76],
       [72, 83],
       [52, 57],
       [52, 45],
       [62, 94],
       [46, 82],
       [43, 74],
       [31, 56],
       [35, 70],
       [41, 94]])]

…同样,要将此应用于大型2D数组,只需迭代它们:

np.random.seed(42)
X = np.random.randint(-100, 100, size=(100, 10))
Y = np.random.randint(-40, 140, size=(100, 10))

bincol = ['r', 'g', 'b', 'y', 'k']

for xr, yr in zip(X, Y):
    for i, binned_points in enumerate(bin_points(xr, yr)):
        ax.scatter(*binned_points.T, c=bincol[i], marker='o' if i<4 else 'x')

enter image description here

这是一个我把它分成省略号的版本。由于OP使用的是简单的几何形状,因此可以用一个简单的公式来测试,即不必“询问”补丁。我将它推广到n个弧,但有一个小缺点,即bin编号不是从左到右,但这可以在其他地方处理。 输出为类型

[ [ [x,y], [x,y],...], ... ] 

即每个箱子的x、y清单。这里的编号是从-3到3,0在外面。在

^{pr2}$

显示

test data

注意,我使用了关于x=0的对称性。如果省略号相对于x移动,则代码必须稍作修改。 还要注意,提供省略号的顺序很重要!在

相关问题 更多 >