使用pd.Grouper按时间索引分组绘图时遇多类错误的问题
我来帮你一步步拆解你遇到的问题,同时给出能直接运行的解决办法~
首先,先明确核心问题原因
你遇到的几个错误,本质都是因为**pd.Grouper和旧的pd.TimeGrouper在处理索引时的逻辑差异**,以及后续绘图时的版本兼容/索引解析问题:
1. KeyError: 'The grouper name Time is not found'
旧的pd.TimeGrouper会自动识别DatetimeIndex,但pd.Grouper的key参数是用来指定DataFrame的列名的——而你的Time是索引,不是普通列,所以直接写key='Time'当然找不到。
2. TypeError: unhashable type: 'DatetimeIndex'
直接把df_final.index传入groupby不行,因为groupby需要的是分组器(比如pd.Grouper)或者列名/序列,不能直接传整个DatetimeIndex对象。
3. NameError: name '_converter' is not defined
这个报错通常是pandas和matplotlib版本不兼容,或者分组后的时间索引在绘图时无法被matplotlib正确解析导致的,重置索引为普通列就能绕过这个问题。
具体解决步骤
第一步:正确实现按15分钟分组
首先,先构造你的测试DataFrame(方便复现):
import pandas as pd data = { 'Response': [201, 400, 200, 400, 200], 'Time': pd.to_datetime(['2018-01-14 00:00:00']*5) } df_final = pd.DataFrame(data).set_index('Time')
然后用两种正确的方式替换旧的TimeGrouper:
方式一:用level参数指定索引名
因为你的DatetimeIndex名字是Time,直接用level='Time'告诉pd.Grouper去索引里找分组依据:
# 按15分钟+响应码分组,统计数量,再把响应码转成列(unstack) grouped = df_final.groupby([ pd.Grouper(level='Time', freq='15min'), 'Response' ]).size().unstack(fill_value=0)
方式二:直接按索引频率分组
pd.Grouper默认会识别行索引(axis=0),所以可以省略level,直接指定freq:
grouped = df_final.groupby([ pd.Grouper(freq='15min'), 'Response' ]).size().unstack(fill_value=0)
这两种方式都能得到正确的分组统计结果。
第二步:解决绘图时的_converter错误
分组完成后,直接调用grouped.plot()可能会触发报错,我们可以把分组后的时间索引重置为普通列,让matplotlib能正确解析:
import matplotlib.pyplot as plt # 把Time索引转成普通列 grouped_reset = grouped.reset_index() # 绘制统计图(这里用柱状图举例,你可以换成line等其他类型) ax = grouped_reset.plot( x='Time', kind='bar', figsize=(10, 6), title='Response Codes by 15-Minute Intervals' ) ax.set_xlabel('Time Interval') ax.set_ylabel('Count') plt.xticks(rotation=45) # 旋转x轴标签避免重叠 plt.tight_layout() # 自动调整布局 plt.show()
如果你不想重置索引,也可以尝试更新pandas和matplotlib到稳定版(比如pandas>=1.3,matplotlib>=3.4),新版本修复了很多旧的时间绘图兼容问题。
完整可运行代码
把上面的步骤整合起来,完整代码如下:
import pandas as pd import matplotlib.pyplot as plt # 构造数据 data = { 'Response': [201, 400, 200, 400, 200], 'Time': pd.to_datetime(['2018-01-14 00:00:00']*5) } df_final = pd.DataFrame(data).set_index('Time') # 按15分钟+响应码分组 grouped = df_final.groupby([ pd.Grouper(freq='15min'), 'Response' ]).size().unstack(fill_value=0) # 重置索引并绘图 grouped_reset = grouped.reset_index() ax = grouped_reset.plot(x='Time', kind='bar', figsize=(10, 6)) ax.set_title('Response Code Distribution (15-Min Intervals)') ax.set_xlabel('Time') ax.set_ylabel('Number of Responses') plt.xticks(rotation=45) plt.tight_layout() plt.show()
内容的提问来源于stack exchange,提问作者amadain

