
你在社交媒体或创作社群里大概率刷到过这样一类内容一张 3×3 的九宫格横向代表某种态度倾向纵向代表另一种关系属性每个格子里放着名字或标签旁边再配一条从冷色渐变到暖色的“光谱”。在兴趣社群和内容创作圈子里这类形式常被用来整理“我对不同人的印象”“前后辈之间的关系结构”甚至被用来梳理一个项目中不同角色的归属。表面看这不过是把直觉画成一张图但切换到工程视角你会发现它背后是一套非常完整的分类建模流程定义维度、收集数据、标准化打分、离散分箱、连续光谱映射、最终可视化输出。这篇文章想先给一个明确判断九宫格、印象坐标、光谱这三样东西看起来是同一个模板里的三个元素实际上对应的是三种不同层级的建模方式。九宫格是离散分类印象坐标是连续数值定位光谱是数值到视觉颜色的映射。很多人做类似整理时容易翻车不是因为不会画图而是没有把这三层逻辑拆开。只要把这三层拆清楚这套方法就能从一个“好看的模板”变成可复用、可解释、可协作的分析工具。本文会从一个最小数据集出发用 Python 实现一份足够完整的通用脚本输入若干成员的名字、两个维度的评分、所属阵营自动输出九宫格分区、坐标表并生成带光谱渐变背景的可视化图片。最后会讨论这套方法放到团队管理、内容运营、用户画像等真实项目里需要注意的各种坑。如果你正在做类似的内容整理、活动策划前的成员盘点或者只是想把“凭感觉分类”变成“有依据的分类”这篇文章值得读完。不是要否定直觉而是要让直觉变得可以被讨论、被更新、被复用。1. 这篇文章真正要解决的问题大多数人第一次想做“阵营九宫格”或“印象坐标”第一反应是打开画图工具手动画一个表格然后凭印象把名字拖进格子里。这个流程在人数很少的时候完全没有问题但一旦超过十个人或者需要反复调整问题就冒出来了。第一个痛点是无法增量更新。今天你给 A 放在“高-高”格子明天因为一次合作或一次沟通你的判断变了但你需要在画图软件里重新拖动位置整个图都要重做。第二痛点是坐标含义不统一。你觉得横轴是“观点接近度”别人以为横轴是“能力高低”一张图在不同人眼里解释完全不同。第三痛点是离散和连续被混为一谈。九宫格是离散分类颜色是连续映射坐标点是连续值如果没有把三者分开处理最后画出来的图既不像分类图也不像定位图。更隐蔽的问题是不可解释性。某些名字放在边界位置时如果被问“为什么这个人放在这里”你只能回答“感觉是这样”。感觉当然重要但感觉如果不能转化成数字、证据和可回放的过程就无法在团队里形成共识。这篇文章要解决的就是把“凭感觉画九宫格”升级成“用数据流程做九宫格”。具体来说包含四件事设计两个可解释的维度用来替代“感觉”。为每个对象给出连续评分而不是直接拍一个格子。将连续评分映射到九宫格离散分区同时保留坐标与光谱信息。用脚本自动输出结果方便后续增删改也方便其他人复核。这套方法不只是给同人内容整理或社交关系分类用它同样可以迁移到竞品分析、用户画像、成员盘点、内容选题分布、团队关系诊断等场景。本质是一样的对象在二维空间里定位按区域给出分类标签再用视觉化方式呈现。2. 核心概念阵营九宫格、印象坐标与光谱到底分别是什么很多人在概念层面就对九宫格产生了误解。九宫格不是一个坐标轴箱子而是一种“对二维空间做离散分区”的视觉表达。它先把连续空间切成 3×3 的格子再给每个格子起一个名字。这个名字才是分类结果。印象坐标则完全不同。它是一个对象在二维连续空间里的位置比如“立场接近度 0.75熟悉程度 0.32”。这个位置可以落在任何一条格线之外它可以被精确描述并且它不要求你立刻判断属于哪个格子。实际上坐标是一种比九宫格更接近原始信息的表达方式。光谱的作用更像一个“辅助通道”。当分类结果要给观看者留下整体直观印象时可以通过颜色渐变来表示某个维度上的高低趋势。比如冷色表示低值暖色表示高值这样观众能立刻感知到大部分对象是偏左还是偏右整体分布是集中还是分散。三者的关系可以用一张表来对比概念数据类型输出形式主要用途阵营九宫格离散分类标签3×3 格子快速概括用户的整体倾向印象坐标连续数值二维平面上的点精确记录对象在多维空间中的位置光谱连续数值映射到颜色渐变背景或色条表达总体趋势与局部渐变在构建时正确顺序是“先坐标再分箱最后映射颜色”。坐标决定点在哪里分箱决定这个点落在哪个分类颜色负责增强视觉解释。反过来走就会很别扭如果先定格子再填坐标本质上还是在拍脑袋。这里还要解释一个容易混淆的点“前后辈”这类身份信息不应该直接作为维度。身份是一种离散属性它可能和“熟悉程度”高度相关但直接把它作为 Y 轴会简化掉很多东西。更推荐的做法是把身份信息作为标签保留在数据中而坐标轴使用更连续、更行为化的维度例如“互动频率”“认识深度”“观点一致性”“工作协作紧密程度”。这样一来你的九宫格不会因为某个人的身份标签变化而失控也不会产生“前辈就一定在某个格子”的错误预设。3. 环境准备与前置条件本文示例在 Python 环境下运行依赖相对轻量核心只有 pandas、numpy、matplotlib 三件套。如果你经常做数据分析这些库应该已经存在如果没有用 pip 安装即可。建议使用 Python 3.8 及以上版本不过这里的核心逻辑对版本并不敏感不需要特别新的特性。pandas 和 matplotlib 建议使用当前官方稳定版具体版本号无需严格固定示例代码只用到了非常稳定的 API。创建项目目录时建议按下面的结构组织nine-grid/ ├── profiles.csv ├── nine_grid.py └── output/ └── nine_grid.png其中profiles.csv是输入数据文件nine_grid.py是核心脚本output目录用于保存生成的图片。安装依赖的命令pip install pandas numpy matplotlib如果是在国内网络环境可以临时指定镜像源但这不是必须步骤。安装完成后可以用下面的命令确认版本python -c import pandas, numpy, matplotlib; print(pandas.__version__, numpy.__version__, matplotlib.__version__)这一步只是为了确认环境已经就绪版本号不完全一致也不影响执行。4. 核心流程拆解从原始印象到九宫格坐标图理解了概念之后我们需要把整个流程拆成可以编码的步骤。这里最关键的一点是不要一上来就写代码先想清楚七个环节。4.1 明确两个维度的语义每个九宫格图都需要两个轴轴的含义决定了整个结果有没有解释力。维度必须满足三个条件可理解、可打分、相对独立。例如“立场接近度”和“熟悉程度”就是一组比较常见的组合。前者描述你与对象在观点、风格、目标上的一致程度后者描述你们之间的互动深度。两个维度不要高度重合否则所有点都会挤在对角线上失去分区意义。4.2 设计数据表格每个对象需要四个字段名称、所属阵营、维度一得分、维度二得分。阵营信息先保留为标签不参与坐标计算。分数建议选择 0 到 100 的范围方便所有人统一打分口径。4.3 标准化为 0 到 1 的数值范围不同人打分习惯不同有人喜欢集中在 60 到 80有人喜欢从 10 到 90如果不做标准化九宫格分箱会失真。最常见的做法是 min-max 标准化把两个维度分别映射到 0 到 1。这样后续计算九宫格时会比较稳定。4.4 分箱从连续值到九宫格标准化之后每个点都有 x_norm 和 y_norm 两个值。按照项目需求把 0 到 1 分成三段例如“低、中、高”阈值可以取 0.33 和 0.66也可以根据项目情况调整。这里需要说明阈值本身是策略不是公式。想让分类更严格就提高中档阈值想让分类更宽松就降低阈值。4.5 映射光谱颜色九宫格背景的颜色应该表达“从低到高”的趋势而不是表达某个对象的属性。颜色映射可以基于格子编号或者格子中心坐标使用 matplotlib 的 colormap 实现。通常九宫格 9 个格子的颜色需要从左上角到右下角形成渐变。4.6 绘制散点与标注每个对象最终以散点的形式出现在二维坐标中点的位置由原始坐标标准化后决定而不是由格子位置决定。这样才能保留更多信息即使两个人在同一个格子他们的具体位置也可能看出差别。所有点绘制完成后再为每个点添加名字标签。4.7 输出与存档最后要把结果保存为 PNG 图片同时把中间计算表打印出来或者导出为 CSV。这一步真正做到“图是一份结果数据是原始资产”。下次要调整只需要改数据不用重画图。5. 完整示例代码实现下面进入实战部分。我们会先准备一份示例数据然后编写核心脚本最后执行并验证结果。5.1 准备示例数据在项目目录下创建profiles.csv内容如下name,camp,x_score,y_score 阿明,同盟,20,80 小舟,中立,45,55 小禾,对手,90,30 阿澈,同盟,70,76 小棠,中立,30,66 阿岚,对手,55,40 小满,中立,82,85 阿泽,同盟,38,92 小南,对手,15,35字段说明name对象名称可以是人名、成员 ID、角色名也可以换成竞品名称。camp阵营标签本文不做模型预测只作为附加分类信息。x_score维度一得分本例是“立场接近度 / 观点一致性”0 表示非常不一致100 表示非常一致。y_score维度二得分本例是“熟悉程度 / 互动频率”0 表示完全陌生100 表示非常熟悉。这份数据可以随时替换成你自己的真实业务数据列名保持不变即可。5.2 核心脚本数据加载、标准化与分箱接着在项目目录下创建nine_grid.py。为了避免脚本太长影响阅读我把它拆成两段来说明但最终运行时它们属于同一个文件。第一段是数据加载与计算逻辑# nine_grid.py 第一部分数据加载与九宫格计算 import pandas as pd import numpy as np import matplotlib.pyplot as plt def load_data(path): 读取 CSV 数据文件 return pd.read_csv(path) def normalize(series, min_vNone, max_vNone): 将一列数值标准化到 0-1 之间。 如果提供了 min_v 和 max_v就使用固定上下界 否则使用当前列的最小值和最大值。 if min_v is None: min_v series.min() if max_v is None: max_v series.max() if max_v - min_v 0: return pd.Series([0.5] * len(series)) return (series - min_v) / (max_v - min_v) def grid_level(value, low_threshold0.4, high_threshold0.7): 把 0-1 之间的分数映射为低、中、高三档。 阈值建议根据业务场景调整默认 0.4 和 0.7。 if value low_threshold: return 低 if value high_threshold: return 中 return 高 def build_grid(df, x_min0, x_max100, y_min0, y_max100): 输入原始 DataFrame返回新增坐标列和九宫格标签列的结果表。 result df.copy() result[x_norm] normalize(result[x_score], x_min, x_max) result[y_norm] normalize(result[y_score], y_min, y_max) result[x_level] result[x_norm].apply(grid_level) result[y_level] result[y_norm].apply(grid_level) return result这里需要解释几个设计点。normalize函数采用了可传入固定上下界的写法。默认场景中我们使用 0 到 100 作为上下界这样两次评分具有可比性。如果数据本身就是 0 到 1可以直接使用空参数让函数自动计算。grid_level的默认阈值是 0.4 和 0.7这会导致数据分布呈现“中间窄、两边宽”的效果对大多数印象分类场景比较合适。如果希望分布更均匀可以把阈值改为 0.33 和 0.66或者使用更复杂的等频分箱我在最佳实践部分会再讨论。5.3 核心脚本九宫格可视化与主入口第二段是绘图和主入口部分。为了让九宫格背景有“光谱渐变”效果我们会给每个格子单独填充一个颜色颜色根据格子所在位置从 colormap 中取值。# nine_grid.py 第二部分九宫格绘图与主入口 def draw_nine_grid(df, output_path): 绘制带光谱渐变背景的九宫格散点图。 横轴显示 x_level纵轴显示 y_level点的位置保留原始坐标信息。 fig, ax plt.subplots(figsize(10, 8)) levels [低, 中, 高] # 先绘制九宫格背景 for i, x_level in enumerate(levels): for j, y_level in enumerate(levels): # 根据格子位置取光谱颜色让左上角到右下角形成渐变 color_value 0.1 0.8 * (i j) / 4.0 color plt.cm.RdYlGn(color_value) rect plt.Rectangle( (i, j), 1, 1, facecolorcolor, alpha0.3, edgecolorgray, linewidth1 ) ax.add_patch(rect) # 绘制每个对象的散点位置 for _, row in df.iterrows(): # 将标准化后的连续值映射到 0-2 的坐标范围 x row[x_norm] * 2.0 y row[y_norm] * 2.0 ax.scatter(x, y, s120, colorblack, zorder3) ax.text( x 0.06, y 0.04, row[name], fontsize10, zorder4 ) # 设置坐标轴语义 ax.set_xlim(0, 3) ax.set_ylim(0, 3) ax.set_xticks([0.5, 1.5, 2.5]) ax.set_yticks([0.5, 1.5, 2.5]) ax.set_xticklabels([f{level}维度一 for level in levels]) ax.set_yticklabels([f{level}维度二 for level in levels]) ax.set_xlabel(维度一立场接近度 / 观点一致性) ax.set_ylabel(维度二熟悉程度 / 互动频率) ax.grid(True, linestyle--, alpha0.3) plt.tight_layout() plt.savefig(output_path, dpi150) plt.close() def main(): csv_path profiles.csv output_path output/nine_grid.png df load_data(csv_path) result build_grid(df) # 控制台输出计算表 print( 九宫格坐标计算表 ) print( result[ [name, camp, x_score, y_score, x_norm, y_norm, x_level, y_level] ] ) # 生成图片 draw_nine_grid(result, output_path) print(图片已保存至, output_path) if __name__ __main__: main()这段代码实现了完整流程读取 CSV、计算坐标、分箱、绘图、输出。其中draw_nine_grid函数有几个值得注意的地方。背景色块使用两层循环遍历九个格子每个格子根据(i j) / 4.0计算颜色位置。这意味着左上角格子的颜色偏冷右下角格子的颜色偏暖整体呈现“光谱”的渐变感而不是每格随意填色。散点与格子的关系是两层结构散点坐标是真实的连续值映射而格子是离散分区。这样做的好处是即使 A 和 B 都被分到“高-高”格子A 靠近边界、B 在中心也能从图片位置看出差别。如果只用格子位置来画散点信息就会严重丢失。5.4 运行方式进入项目目录确保profiles.csv与nine_grid.py在同级目录然后执行python nine_grid.py如果output目录不存在可能会因为保存路径报错可以先执行mkdir -p output在真实项目中脚本具备自动创建目录的能力会更友好我这里为了保持代码简单没有在脚本中加自动建目录逻辑。你可以在main()开头加上import os; os.makedirs(output, exist_okTrue)一行解决。6. 运行结果与效果验证执行脚本后控制台会打印一张“九宫格坐标计算表”。对于前面给出的示例数据预期输出大致如下 九宫格坐标计算表 name camp x_score y_score x_norm y_norm x_level y_level 0 阿明 同盟 20 80 0.20 0.80 低 高 1 小舟 中立 45 55 0.45 0.55 中 中 2 小禾 对手 90 30 0.90 0.30 高 低 ...图中会看到一张带渐变色背景的九宫格黑色散点分布在对应位置每个点旁边有名字标签。垂直方向从下到上是“低、中、高”熟悉程度水平方向从左到右是“低、中、高”立场接近度。如何判断运行成功控制台没有抛异常且打印出了完整的计算表。output/nine_grid.png文件已生成。图片中每个点的位置和计算表中的x_norm、y_norm基本吻合。如果运行失败第一步不是检查绘图逻辑而是检查以下三件事profiles.csv是否在当前目录列名是否为name,camp,x_score,y_score。是否已经安装 pandas、numpy、matplotlib。output目录是否存在。这套脚本最大的价值不在“跑通一次”而在于重复跑。当你的印象发生变化只需要修改 CSV 中的分数再执行一次python nine_grid.py所有结果都会自动更新。这就是数据化整理比手动画图更重要的原因。7. 常见问题与排查方法实践中不同类型的数据和场景会遇到一些重复出现的问题我把最主要的几个整理成了排查表。问题现象可能原因排查方式解决方案所有点都挤在角落两个维度高度相关或者打分尺度没有拉开查看 x_score 和 y_score 的分布计算相关系数更换维度设计或者重新校准打分尺度九宫格标签不准确很多人被分到“中”阈值设置不合理数据集中在中间区域打印 x_norm 和 y_norm 分布观察分位数改用 0.33/0.66 阈值或按等频分箱图片保存时报错找不到路径output目录不存在查看报错信息中的文件路径提前创建目录或脚本中增加自动建目录逻辑不同人打分差别很大缺少统一的评分参考标准检查每个分数背后是否有备注或锚点说明提前定义 0、50、100 分分别代表什么情况散点位置和格子不一致绘图时使用了格子坐标而不是标准化连续坐标检查绘图部分是否使用了 row[x_norm]统一使用标准化数值映射到 0-2 范围背景色看不出光谱渐变颜色计算方式过于单调检查 color_value 的计算公式根据格子索引 i、j 生成范围 0 到 1 的颜色值CSV 中文乱码文件编码不是 UTF-8用编辑器查看文件编码另存为 UTF-8 编码或读取时指定 encodingutf-8在这些问题中最容易被忽略的是“打分没有统一标准”。如果你让三个人分别打分A 的 70 分可能等于 B 的 85 分这就是为什么需要在表格之外再维护一份“评分锚点说明”。例如在“熟悉程度”这一维度上可以定义0 分表示仅知道名字50 分表示有过两三次较深互动100 分表示几乎每天协作。有了锚点数据才有可复核的基础。8. 最佳实践与工程建议代码能跑通只是开始。如果想把“阵营九宫格印象坐标”做成真正能用于团队协作或业务分析的方案还需要在实际使用中注意以下六点。第一维度必须正交。正交在这里不是严格的数学概念而是说两个维度不要描述同一个东西。常见错误是用“关系亲密度”和“互动频率”做两个轴这两个指标高度相关最终所有点都会落在对角线附近九宫格的划分意义不大。建议先想清楚两个维度分别回答什么问题再开始打分。第二保持“连续性优先分箱在后”。很多人在设计阶段就想好“这个人是低-中那个人是高-高”于是直接填格子。这会失去坐标带来的精确性。推荐的流程是先给两个维度打连续分数再用程序分箱。这样即便某个人被分到了“中-中”你也能看到他实际上更靠近“高”还是“低”而不是被一个标签框死。第三建立评分锚点并写入说明文档。你可以在项目目录下放一份README.md把两个维度分别是什么、每个分段代表什么写明八条甚至十条示例。这不是多余工作而是为了让半年后的自己还能理解这张图。主观判断本身没问题但主观判断如果不可追溯就无法演进。第四使用统一量纲。本文用的是 0 到 100实际业务也可以使用 1 到 5 或 1 到 10。一旦确定就不要混用。如果多个人协作打分建议先用同一套参考对象做一次校准再进入正式打分流程。第五把图片和计算表都留下来。图片适合展示计算表适合复查。更稳妥的做法是每次运行后导出一份带时间戳的 CSV例如result_20250601.csv。这样当九宫格发生变化时你能回看之前某个人在某个时间点的坐标而不是只能看到最新版。第六隐私与边界意识。如果九宫格涉及真实人物、合作伙伴或客户名不要在没有授权的情况下公开发布。即使是内部使用也要明确这份资料的使用边界避免因为一张图引发误解。代码层面不涉及安全配置但数据使用者应该具备边界意识。9. 总结与后后续学习方向回到本文开头的判断九宫格是离散分类印象坐标是连续数值光谱是颜色映射。这套方法的核心不是教你怎么画一张更漂亮的图而是帮你建立“分类之前先定位定位之前先量化”的思维方式。顺着这个思路下一步可以朝几个方向继续深入。如果你想提升分类的科学性可以尝试把本文的手动分箱替换为聚类算法。KMeans 或层次聚类会在二维平面上自动找出自然分组然后你再为每个聚类簇命名属性。这样做会让九宫格的边界更有数据依据。要注意的是聚类结果不一定正好形成 3×3 的结构可能需要你重新调整标签体系。如果你想做实时动态效果可以把静态 matplotlib 图升级为 Plotly 或 Bokeh 交互图支持鼠标悬停查看对象详情。对于内容运营和团队盘点场景交互式热力图往往比静态图更实用。如果你想做更多维度的分析二维平面不够用时就可以引入雷达图或平行坐标图用来展示每个对象在多个维度上的得分。但二维九宫格的优势是直观复杂度提升之后可解释性一定会下降需要做好平衡。在真实项目中我最想提醒你的一点是不要把九宫格结论当作永久判断。人的印象会变化业务关系会变化分数应该跟着更新。这套脚本的价值就在于它把更新成本压到了极低。只要及时维护数据九宫格就能从一张静态结论图变成一个动态观察窗口。建议收藏备用。下一次需要给团队做盘点、给内容做分层或者只是想系统整理一下自己对一群人的认知时可以打开这个脚本把 CSV 换成你的真实数据跑一遍试试。