如何为散点图设置bin坐标

时间:2018-06-12 07:00:45

标签: python pandas matplotlib histogram scatter

我想返回占据特定区域的散点数。通常,我会使用2dhistogrampcolormesh执行此操作。

但是如果我想设置代表网格的不规则尺寸的bin坐标,我该怎么做?

以下是我的数据集示例。

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

x1 = np.random.randint(80, size=(400, 10))
y1 = np.random.randint(80, size=(400, 10))

x2 = np.random.randint(80, size=(400, 10))
y2 = np.random.randint(80, size=(400, 10))

fig, ax = plt.subplots()
ax.grid(False)

plt.scatter(x1[0],y1[0], c = 'r', zorder = 2)
plt.scatter(x2[0],y2[0], c = 'b', zorder = 2)

ang1 = 0, 50
ang2 = 100, 50
angle = math.degrees(math.acos(5.5/9.15))
xy = 50, 50

Halfway = mpl.lines.Line2D((50,50), (0,100), c = 'white')
arc1 = mpl.patches.Arc(ang1, 65, 100, angle = 0, theta2 = angle, theta1 = 360-angle, lw = 2)
arc2 = mpl.patches.Arc(ang2, 65, 100, angle = 0, theta2 = 180+angle, theta1 = 180-angle, lw = 2)
Oval = mpl.patches.Ellipse(xy, 100, 100, lw = 3, alpha = 0.1)

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

plt.text(15, 75, '1', fontsize = 8)
plt.text(35, 90, '2', fontsize = 8)
plt.text(65, 90, '3', fontsize = 8)
plt.text(85, 75, '4', fontsize = 8)

ax.autoscale()

plt.draw()

我要设置的垃圾箱标有1-4。是否可以设置返回这些箱的坐标?

如果我可以设置这些坐标,那么我想返回每个散点所占据的bin。 输出: enter image description here 更新:

如果我想要在散点图中的每一行的每个bin中显示xy的导出,我会写出(x1[0], y1[0])并转置数据以返回:

          1             2            3             4   
0  [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)]

然后我会将(x1[0], y1[0])更改为(x1[1], y1[1])以获取第二行数据。

          1             2            3             4   
1  [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)]

然后我会把它们组合起来创建:

          1             2            3             4   
0  [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)]  
1  [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)]

我已经有1000行,所以我试图创建一个方法来使用整个(x1, y1)来为每行数据生成每个bin中的坐标。 / p>

预期输出:

          1             2            3             4   
0  [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)]
1  [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)]
2  [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)]    
3  [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)]
4  [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)] [(x,y),(x,y)]
5....
6....

如果我尝试(x1, y1),我会收到错误:

err = (arc_vertices[:,0] - x)**2 + (arc_vertices[:,1] - y)**2 ValueError: operands could not be broadcast together with shapes (70,) (10,)

1 个答案:

答案 0 :(得分:1)

我对这种做法真的不满意。计算具有数据x坐标的点的y坐标在曲线上的位置似乎更好。

这种方法类似,但使用弧的有限顶点:

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

for (x,y) in zip(x1[0], y1[0]):
    err = (arc1v[:,0] - x)**2 + (arc1v[:,1] - y)**2
    nearest = (arc1v[err == min(err)])[0]
    line_x = (x, nearest[0])
    line_y = (y, nearest[1])
    ax.add_line(mpl.lines.Line2D(line_x, line_y))

    if x > nearest[0]:
        ax.scatter(x, y, marker='^', s=100, c='k', zorder=1)
    else:
        ax.scatter(x, y, marker='v', s=100, c='k', zorder=1)

这"标签" (左)曲线左侧的点,带有一个朝下的三角形,右侧的点带有一个朝上的三角形。图表上的线指向曲线上最近的定义顶点,仅供说明使用。

您也可以为另一条曲线执行此操作,并且bin 2/3分区很简单。

这是一个示例输出图: enter image description here

更新

这是一个更完整的答案:

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


BIN_23_X = 50               # The separator between bin 2 and 3

x1 = np.random.randint(80, size=(400, 10))
y1 = np.random.randint(80, size=(400, 10))

x2 = np.random.randint(80, size=(400, 10))
y2 = np.random.randint(80, size=(400, 10))

fig, ax = plt.subplots()
ax.grid(False)

plt.scatter(x1[0],y1[0], c = 'r', zorder = 2)
plt.scatter(x2[0],y2[0], c = 'b', zorder = 2)

ang1 = 0, 50
ang2 = 100, 50
angle = math.degrees(math.acos(5.5/9.15))
xy = 50, 50

Halfway = mpl.lines.Line2D((BIN_23_X,BIN_23_X), (0,100), c = 'white')
arc1 = mpl.patches.Arc(ang1, 65, 100, angle = 0, theta2 = angle, theta1 = 360-angle, lw = 2)
arc2 = mpl.patches.Arc(ang2, 65, 100, angle = 0, theta2 = 180+angle, theta1 = 180-angle, lw = 2)
Oval = mpl.patches.Ellipse(xy, 100, 100, lw = 3, alpha = 0.1)

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

plt.text(15, 75, '1', fontsize = 8)
plt.text(35, 90, '2', fontsize = 8)
plt.text(65, 90, '3', fontsize = 8)
plt.text(85, 75, '4', fontsize = 8)

# Classification helpers
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:                         # Is this point in bin 1?  To the left of arc1?
            bins[1].append((x,y))
        elif x > nx2:                       # Is this point in bin 4?  To the right of arc2?
            bins[4].append((x,y))
        else:
            # If we get here, the point is in either bin 2 or 3.  We'll consider points
            #   that fall on the line to be in bin 3.
            if x < BIN_23_X:                # Is this point to the left BIN_23_X? => Bin 2
                bins[2].append((x,y))
            else:                           # Otherwise, the point is in Bin 3
                bins[3].append((x,y))

    return bins

# Classify points
bins_red  = classify_pointset(x1[0], y1[0])
bins_blue = classify_pointset(x2[0], y2[0])

# Display classifications
print("Red:")
for x in bins_red.items():
    print(" ", x)

print("Blue:")
for x in bins_blue.items():
    print(" ", x)

# "Annotate" classifications
for (x,y) in (bins_red[1] + bins_blue[1]):
    ax.scatter(x, y, marker='^', s=100, c='k', zorder=1)

for (x,y) in (bins_red[2] + bins_blue[2]):
    ax.scatter(x, y, marker='v', s=100, c='k', zorder=1)

for (x,y) in (bins_red[3] + bins_blue[3]):
    ax.scatter(x, y, marker='^', s=100, c='y', zorder=1)

for (x,y) in (bins_red[4] + bins_blue[4]):
    ax.scatter(x, y, marker='v', s=100, c='y', zorder=1)


ax.autoscale()

plt.draw()
plt.show()

产地:

enter image description here

这里,积分是&#34;注释&#34;在它们后面的形状对应于它们被分类到哪些箱子中:

Bin       Anno. Color     Triangle Pointing
-------------------------------------------
Bin 1     Black           Up
Bin 2     Black           Down
Bin 3     Yellow          Up
Bin 4     Yellow          Down

代码还会显示分类结果(classify_pointset的输出是一个dict,键入bin编号(1-4),其值是找到位于bin中的点的坐标点

Red:
  (1, [(14, 30), (4, 18), (12, 48)])
  (2, [(49, 41)])
  (3, [(62, 79), (50, 7), (68, 19), (71, 1), (59, 27), (77, 0)])
  (4, [])
Blue:
  (1, [(20, 74), (11, 17), (12, 75)])
  (2, [(41, 19), (30, 15)])
  (3, [(61, 75)])
  (4, [(79, 73), (69, 58), (76, 34), (78, 65)])

您不必以图形方式注释图形,只是为了说明,您可以使用classify_pointsetbins_red和{{1}返回的字符串})。

更新2

以下代码生成列表列表(仍为1索引),因此您可以通过访问bins_blue找到bin 1中的所有点(红色和蓝色)。 all_points[1]列表中的第一个元素(索引0)为all_points,因为我们将列表保持为1索引。

None

输出:

0 None
1 [(1, 8), (16, 72), (23, 67), (12, 19), (24, 51), (24, 47), (15, 23), (18, 51)]
2 [(39, 75), (35, 27), (48, 55), (45, 53), (45, 22)]
3 [(66, 58), (55, 64), (70, 1), (71, 15), (73, 3), (71, 75)]
4 [(74, 62)]