如何将datetime.time类型数据绘制在Matplotlib的X轴上
问题:按时段汇总步数后无法用Matplotlib绘图
我想对比设备在不同时段的使用情况,数据集提供了YYYY/MM/D HH:MM:SS格式的日期时间数据,我把它拆分为time和date两列,然后按time列分组得到每小时的步数汇总数据。
import pandas as pd import matplotlib.pyplot as plt # 示例数据集 data = {'Id': [1503960366, 1503960366, 1503960366, 1503960366, 1503960366], 'ActivityHour': ['2016-04-12 00:00:00', '2016-04-12 01:00:00', '2016-04-12 02:00:00', '2016-04-12 03:00:00', '2016-04-12 04:00:00'], 'Calories': [81, 61, 59, 47, 48], 'TotalIntensity': [20, 8, 7, 0, 0], 'AverageIntensity': [0.333333, 0.133333, 0.116667, 0.0, 0.0], 'StepTotal': [373, 160, 151, 0, 0]} hourly_activity = pd.DataFrame(data) hourly_activity['time'] = [d.time() for d in hourly_activity['ActivityHour']] hourly_activity['date'] = [d.date() for d in hourly_activity['ActivityHour']] hours_vs_steps = hourly_activity.copy() hours_vs_steps = hours_vs_steps[['time', 'StepTotal', 'Calories']] hours_vs_steps = hours_vs_steps.groupby(['time']).sum() hours_vs_steps.reset_index(inplace=True)
初始DataFrame
Id ActivityHour Calories TotalIntensity AverageIntensity StepTotal 0 1503960366 2016-04-12 00:00:00 81 20 0.333333 373 1 1503960366 2016-04-12 01:00:00 61 8 0.133333 160 2 1503960366 2016-04-12 02:00:00 59 7 0.116667 151 3 1503960366 2016-04-12 03:00:00 47 0 0.000000 0 4 1503960366 2016-04-12 04:00:00 48 0 0.000000 0
分组汇总后的数据
time StepTotal Calories 0 00:00:00 39404 67066 1 01:00:00 21555 65464 2 02:00:00 15964 64551 3 03:00:00 5996 63013 4 04:00:00 11836 63620
我尝试用Matplotlib或Seaborn把StepTotal作为Y轴、time作为X轴绘图,不管是直接用列还是把time设为索引都失败了。执行plt.plot(hours_vs_steps.time, hours_vs_steps.StepTotal)时抛出如下错误:
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[28], line 1 ----> 1 plt.plot(hours_vs_steps.time, hours_vs_steps.StepTotal) File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\pyplot.py:2812, in plot(scalex, scaley, data, *args, **kwargs) 2810 @_copy_docstring_and_deprecators(Axes.plot) 2811 def plot(*args, scalex=True, scaley=True, data=None, **kwargs): -> 2812 return gca().plot( 2813 *args, scalex=scalex, scaley=scaley, 2814 **({"data": data} if data is not None else {}), **kwargs) File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\axes\_axes.py:1690, in Axes.plot(self, scalex, scaley, data, *args, **kwargs) 1688 lines = [*self._get_lines(*args, data=data, **kwargs)] 1689 for line in lines: -> 1690 self.add_line(line) 1691 if scalex: 1692 self._request_autoscale_view("x") File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\axes\_base.py:2304, in _AxesBase.add_line(self, line) 2301 if line.get_clip_path() is None: 2302 line.set_clip_path(self.patch) -> 2304 self._update_line_limits(line) 2305 if not line.get_label(): 2306 line.set_label(f'_child{len(self._children)}') File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\axes\_base.py:2327, in _AxesBase._update_line_limits(self, line) 2323 def _update_line_limits(self, line): 2324 """ 2325 Figures out the data limit of the given line, updating self.dataLim. 2326 """ -> 2327 path = line.get_path() 2328 if path.vertices.size == 0: 2329 return File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\lines.py:1029, in Line2D.get_path(self) 1027 """Return the `~matplotlib.path.Path` associated with this line.""" 1028 if self._invalidy or self._invalidx: -> 1029 self.recache() 1030 return self._path File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\lines.py:657, in Line2D.recache(self, always) 655 if always or self._invalidx: 656 xconv = self.convert_xunits(self._xorig) -> 657 x = _to_unmasked_float_array(xconv).ravel() 658 else: 659 x = self._x File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\cbook\__init__.py:1335, in _to_unmasked_float_array(x) 1333 return np.ma.asarray(x, float).filled(np.nan) 1334 else: -> 1335 return np.asarray(x, float) TypeError: float() argument must be a string or a real number, not 'datetime.time'
解决方案
错误根源是Matplotlib无法直接处理datetime.time类型的X轴数据,需要把时间转换成字符串/数值格式,或者改用pandas的绘图接口(它能更好地处理时间类型)。
方法1:用pandas内置绘图接口(最简单)
pandas的plot()方法可以自动识别时间格式,直接调用即可:
hours_vs_steps.plot(x='time', y='StepTotal', kind='line') plt.show()
方法2:将time列转为字符串
把datetime.time对象转换成字符串,Matplotlib就能正常识别:
# 转换time列为字符串 hours_vs_steps['time_str'] = hours_vs_steps['time'].astype(str) # 绘图 plt.plot(hours_vs_steps['time_str'], hours_vs_steps['StepTotal']) plt.xlabel('时段') plt.ylabel('总步数') plt.title('不同时段步数汇总') plt.xticks(rotation=45) # 旋转X轴标签避免重叠 plt.show()
方法3:优化数据处理流程(从源头避免问题)
在拆分日期时间时,先把ActivityHour转为datetime类型(你的原始代码漏了这一步,实际运行会报错),再提取格式化的时间字符串或小时数值:
# 先转换为datetime类型 hourly_activity['ActivityHour'] = pd.to_datetime(hourly_activity['ActivityHour']) # 提取小时字符串,比如"00:00" hourly_activity['time'] = hourly_activity['ActivityHour'].dt.strftime('%H:%M') # 也可以直接提取小时数值(0-23) # hourly_activity['hour'] = hourly_activity['ActivityHour'].dt.hour # 后续分组汇总 hours_vs_steps = hourly_activity[['time', 'StepTotal', 'Calories']].groupby(['time']).sum().reset_index() # 直接绘图 plt.plot(hours_vs_steps['time'], hours_vs_steps['StepTotal']) plt.show()
内容的提问来源于stack exchange,提问作者dbeltranor
相关产品推荐
相关产品推荐

