You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Matplotlib从分类网格点绘制多边形相图?

生成带清晰线性边界的相图

我有一个包含1681个均匀分布2D网格点的DataFrame,每个点包含x、y坐标、类别(相)标签及对应颜色。目前只能绘制简单散点图,需要生成带有清晰线性边界的相图,边界取不同类别相邻点的中间位置,可使用插值方法,推测matplotlib.patches.Polygon能派上用场。注意:测试数据生成代码里的多边形信息不能用于相图生成。

数据示例

x     y      label    color
0    -40.0 -30.0         Fe  #660066
1    -40.0 -29.0         Fe  #660066
2    -40.0 -28.0        FeS  #ff7f50
...    ...   ...        ...      ...
1680   0.0  10.0  Fe2(SO4)3  #8a2be2

[1681 rows x 4 columns]

现有散点图实现代码

import matplotlib.pyplot as plt
import pandas as pd

plt.figure(figsize=(8., 8.))
for color in df.color.unique():
    df_color = df[df.color==color]
    plt.scatter(
            x=df_color.x,
            y=df_color.y,
            c=color,
            s=100,
            label=df_color.label.iloc[0]
    )
plt.xlim([-40., 0.])
plt.ylim([-30., 10.])
plt.xlabel('Log pO2(g)')
plt.ylabel('Log pSO2(g)')
plt.legend(bbox_to_anchor=(1.05, 1.))
plt.show()

测试数据生成代码

import numpy as np
import pandas as pd
from shapely.geometry import Point, Polygon

labels = ['Fe', 'Fe3O4', 'FeS', 'Fe2O3', 'FeS2', 'FeSO4', 'Fe2(SO4)3']
colors = ['#660066', '#b6fcd5', '#ff7f50', '#ffb6c1', '#c6e2ff', '#d3ffce', '#8a2be2']
polygons = []
polygons.append(Polygon([(-26.7243,-14.7423), (-26.7243,-30.0000), (-40.0000,-30.0000), (-40.0000,-28.0181)]))
polygons.append(Polygon([(-18.1347,-0.4263), (-16.6048,1.6135), (-16.6048,-30.0000), (-26.7243,-30.0000), (-26.7243,-14.7423), (-18.1347,-0.4263)]))
polygons.append(Polygon([(-18.1347,-0.4263), (-26.7243,-14.7423), (-40.0000,-28.0181), (-40.0000,-22.2917), (-18.1347,-0.4263)]))
polygons.append(Polygon([(0.0000,-20.2615), (0.0000,-30.0000), (-16.6048,-30.0000), (-16.6048,1.6135), (-16.5517,1.6865), (-6.0517,-0.9385), (0.0000,-3.9643)]))
polygons.append(Polygon([(-14.2390,10.0000), (-14.5829,7.5927), (-16.5517,1.6865), (-16.6048,1.6135), (-18.1347,-0.4263), (-40.0000,-22.2917), (-40.0000,10.0000)]))
polygons.append(Polygon([(-6.0517,-0.9385), (-16.5517,1.6865), (-14.5829,7.5927), (-6.0517,-0.9385)]))
polygons.append(Polygon([(0.0000,-3.9643), (-6.0517,-0.9385), (-14.5829,7.5927), (-14.2390,10.0000), (0.0000,10.0000)]))

x_grid = np.arange(-40., 0.01, 1.)
y_grid = np.arange(-30., 10.01, 1.)
xy_grid = np.array(np.meshgrid(x_grid, y_grid)).T.reshape(-1, 2).tolist()
data = []
for coords in xy_grid:
    point = Point(coords)
    for i, poly in enumerate(polygons):
        if poly.buffer(1e-3).contains(point):
            data.append({
                'x': point.x,
                'y': point.y,
                'label': labels[i],
                'color': colors[i]
            })
            break
df = pd.DataFrame(data)

解决方案

核心思路:先识别不同类别相邻网格点的中点作为边界参考,再为每个类别拟合多边形轮廓,用Polygon绘制填充区域与边界。

实现代码

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from matplotlib.patches import Polygon
from scipy.spatial import ConvexHull

# 1. 将数据转换为网格矩阵,方便邻点查找
x_unique = sorted(df.x.unique())
y_unique = sorted(df.y.unique())
x_idx = {x:i for i,x in enumerate(x_unique)}
y_idx = {y:i for i,y in enumerate(y_unique)}

label_grid = np.full((len(y_unique), len(x_unique)), '', dtype=object)
color_grid = np.full((len(y_unique), len(x_unique)), '', dtype=object)

for _, row in df.iterrows():
    i = x_idx[row['x']]
    j = y_idx[row['y']]
    label_grid[j, i] = row['label']
    color_grid[j, i] = row['color']

# 2. 收集不同类别相邻点的中点(边界候选)
boundary_midpoints = []
# 只检查右、下方向邻点,避免重复计算
for j in range(len(y_unique)):
    for i in range(len(x_unique)):
        current_label = label_grid[j, i]
        # 右侧邻点
        if i < len(x_unique)-1:
            right_label = label_grid[j, i+1]
            if current_label != right_label:
                boundary_midpoints.append(((x_unique[i]+x_unique[i+1])/2, y_unique[j]))
        # 下方邻点
        if j < len(y_unique)-1:
            down_label = label_grid[j+1, i]
            if current_label != down_label:
                boundary_midpoints.append((x_unique[i], (y_unique[j]+y_unique[j+1])/2))

# 3. 绘制相图
plt.figure(figsize=(8, 8))

# 为每个类别生成多边形并绘制
for label in df.label.unique():
    class_points = df[df.label == label][['x', 'y']].values
    # 用凸包拟合类别外轮廓,若需更精细边界可加入边界中点优化
    if len(class_points) >= 3:
        hull = ConvexHull(class_points)
        poly_vertices = class_points[hull.vertices]
        color = df[df.label == label]['color'].iloc[0]
        # 绘制填充区域+黑色边界
        poly = Polygon(poly_vertices, facecolor=color, edgecolor='black', linewidth=1.5, alpha=0.8)
        plt.gca().add_patch(poly)

# 可选:叠加原始网格点
# for color in df.color.unique():
#     df_color = df[df.color==color]
#     plt.scatter(df_color.x, df_color.y, c=color, s=20, edgecolor='white')

plt.xlim([-40., 0.])
plt.ylim([-30., 10.])
plt.xlabel('Log pO2(g)')
plt.ylabel('Log pSO2(g)')
# 添加图例
handles = [plt.Rectangle((0,0),1,1, color=df[df.label==lbl]['color'].iloc[0]) for lbl in df.label.unique()]
plt.legend(handles, df.label.unique(), bbox_to_anchor=(1.05, 1.))
plt.tight_layout()
plt.show()

说明

  • 邻点检查:仅遍历右、下方向,避免重复计算相同边界
  • 轮廓拟合:使用ConvexHull快速生成类别外轮廓,适合规则相区域;若相区域有凹形,可改用shapely库进行多边形生成或插值优化
  • 边界优化:可将收集到的边界中点加入对应类别的点集后再做拟合,提升边界精度

内容的提问来源于stack exchange,提问作者Shaun Han

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 12:22:06