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

如何堆叠10+子图?解决57张子图绘制的ValueError问题

解决Matplotlib子图数量限制的问题

问题描述

我需要绘制57张图表,每张包含两个数据集(下载和上传速度),尝试用子图堆叠显示,但绘制第3张子图时出现ValueError: num must be 1 <= num <= 2, not 3,看起来子图数量有限制。我的代码如下:

for k, v in tests.items(): 
    print("Building graphs for " + str(k)) 
    i = 1 
    while i <= v: 
        print("Building graph for Config " + str(i)) 
        cur.execute('Select Iteration, Download, Upload From Ookla_Client where Config = ' + str(i) + ';') 
        db_return = cur.fetchall() 
        x = [] 
        for test in db_return: 
            x.append(test[0]) 
        y = [] 
        for test in db_return: 
            y.append(test[1]) 
        y2 = [] 
        for test in db_return: 
            y2.append(test[2]) 
        plt.subplot(2,1,i) 
        plt.plot(x,y,'.-') 
        plt.plot(x,y2,'.-') 
        plt.title('Config 1') 
        plt.xlabel('Test') 
        plt.ylabel('Mb/s') 
        plt.legend(['Download', 'Upload'], loc='upper right') 
        plt.grid() 
        i += 1

错误原因

你遇到的问题根源在于plt.subplot(2,1,i)的参数逻辑:plt.subplot(rows, cols, num)里的num参数必须是1到rows*cols之间的整数。你设置的是2行1列的子图布局,最多只能容纳2个子图,当i增加到3时,自然就超出了范围,触发报错。

解决方案

根据你的需求(绘制57张独立图表),推荐两种可行的实现方式:

方法1:每张图表单独创建一个新画布

这是最直接的方案,每次循环都创建一个新的figure对象,这样每张图都是独立的,不会受子图布局的限制。同时注意修正标题里的固定Config 1,改成对应当前的i值:

import matplotlib.pyplot as plt

for k, v in tests.items(): 
    print("Building graphs for " + str(k)) 
    i = 1 
    while i <= v: 
        print("Building graph for Config " + str(i)) 
        cur.execute('Select Iteration, Download, Upload From Ookla_Client where Config = ' + str(i) + ';') 
        db_return = cur.fetchall() 
        # 简化数据提取的代码(可选优化)
        x = [test[0] for test in db_return]
        y = [test[1] for test in db_return]
        y2 = [test[2] for test in db_return]
        
        # 创建新的画布
        plt.figure()
        plt.plot(x,y,'.-') 
        plt.plot(x,y2,'.-') 
        plt.title(f'Config {i}')  # 动态设置标题
        plt.xlabel('Test') 
        plt.ylabel('Mb/s') 
        plt.legend(['Download', 'Upload'], loc='upper right') 
        plt.grid() 
        # 如果需要保存每张图,可以在这里添加plt.savefig(f'config_{i}_speed.png')
        i += 1
plt.show()  # 最后一次性显示所有图表

方法2:使用动态网格布局排列所有子图(适合需要在一个画布展示的场景)

如果你希望把所有图表放在同一个大画布上,可以动态计算合适的行列数(比如9行7列,总共63个位置,足够容纳57张图),然后用plt.subplot(rows, cols, i)来定位:

import matplotlib.pyplot as plt
import math

total_plots = 57  # 或者根据v的值计算总数量
rows = math.ceil(math.sqrt(total_plots))  # 计算行数,也可以手动设置比如9
cols = math.ceil(total_plots / rows)

plt.figure(figsize=(cols*5, rows*4))  # 设置画布大小,避免图表太挤

for k, v in tests.items(): 
    print("Building graphs for " + str(k)) 
    i = 1 
    while i <= v: 
        print("Building graph for Config " + str(i)) 
        cur.execute('Select Iteration, Download, Upload From Ookla_Client where Config = ' + str(i) + ';') 
        db_return = cur.fetchall() 
        x = [test[0] for test in db_return]
        y = [test[1] for test in db_return]
        y2 = [test[2] for test in db_return]
        
        plt.subplot(rows, cols, i) 
        plt.plot(x,y,'.-') 
        plt.plot(x,y2,'.-') 
        plt.title(f'Config {i}', fontsize=8)  # 缩小标题字体避免重叠
        plt.xlabel('Test', fontsize=7) 
        plt.ylabel('Mb/s', fontsize=7) 
        plt.legend(['Download', 'Upload'], loc='upper right', fontsize=6) 
        plt.grid() 
        plt.tick_params(axis='both', labelsize=6)  # 缩小刻度字体
        i += 1
plt.tight_layout()  # 自动调整子图间距
plt.show()

额外优化建议

  • 数据提取部分可以用列表推导式简化,让代码更简洁(如上面示例所示);
  • 如果需要保存图表,建议在循环内添加plt.savefig(),避免一次性显示57张图导致卡顿;
  • 注意SQL语句的安全性,直接拼接str(i)存在SQL注入风险,建议使用参数化查询:cur.execute('Select Iteration, Download, Upload From Ookla_Client where Config = ?;', (i,))(具体语法根据你使用的数据库驱动调整)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:58:44