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

如何调整QDateTimeAxis使首尾数据点不贴轴且不偏移数值?

问题描述

我尝试给QDateTimeAxis添加约1个月的边距,避免图表首尾的数据点紧贴轴边缘,用来展示某公司季度EPS(财务目标)的达标情况。原本图表首尾点紧贴轴边缘,我用以下代码调整轴的最小/最大日期:

self.axisX.setMin(min_date.addDays(-30))
self.axisX.setMax(max_date.addDays(30))

调整后轴的日期范围改变,但数据点仍贴在边缘,且对应日期显示错误。请问问题的原因是什么?

完整代码

import sys
from PySide6.QtCore import Qt
from PySide6.QtGui import QPainter, QColor
from PySide6.QtWidgets import QMainWindow, QApplication
from PySide6.QtCharts import QChart, QChartView, QScatterSeries, QValueAxis, QDateTimeAxis
from PySide6.QtCore import QDateTime
import yfinance as yf
import pprint
import pandas as pd

class TestChart(QMainWindow):
    def __init__(self):
        super().__init__()

        ticker = yf.Ticker("MSFT")

        earnings_dates_df = ticker.earnings_dates
        # Remove time from the datetime object
        # earnings_dates_df.index = earnings_dates_df.index.date

        print(earnings_dates_df)

        # Get the min and maxs of the EPS date for the axis
        min_eps_estimate = earnings_dates_df['EPS Estimate'].min()
        min_eps_actual = earnings_dates_df['Reported EPS'].min()

        max_eps_estimate = earnings_dates_df['EPS Estimate'].max()
        max_eps_actual = earnings_dates_df['Reported EPS'].max()

        # Set the date axis
        self.axisX = QDateTimeAxis()
        self.axisX.setFormat("MM-dd-yyyy")
        self.axisX.setTitleText("Date")

        min_date = QDateTime(earnings_dates_df.index.min())
        max_date = QDateTime(earnings_dates_df.index.max())

        self.axisX.setMin(min_date.addDays(-30))
        self.axisX.setMax(max_date.addDays(30))

        # Set the EPS axis
        self.axisY = QValueAxis()
        if min_eps_estimate < min_eps_actual:
            self.axisY.setMin(min_eps_estimate - (0.15 *  min_eps_estimate))
        else:
            self.axisY.setMin(min_eps_actual - (0.15 *  min_eps_actual))

        if max_eps_estimate > max_eps_actual:
            self.axisY.setMax(max_eps_estimate + (0.15 *  max_eps_estimate))
        else:
            self.axisY.setMax(max_eps_actual + (0.15 *  max_eps_actual))
        self.axisY.setTitleText("EPS $")

        # Create the graphes points for the estimates
        self.estimate_series = QScatterSeries()

        # Create the graphes points for the actuals that MISSED
        self.missed_series = QScatterSeries()
        self.missed_series.setColor(QColor(255, 0, 0))

        # Create the graphes points for the actuals that MADE it.
        self.achieved_series = QScatterSeries()
        self.achieved_series.setColor(QColor(0, 0, 255))

        # iterate over the pandas to get each date and estimate
        for index, row in earnings_dates_df.iterrows():

            # Test for NAN
            if pd.isna(row['EPS Estimate']) or pd.isna(row['Reported EPS']):
                continue

            earnings_date_time = QDateTime(index).toMSecsSinceEpoch()
            print(earnings_date_time)

            # Add ALL of our estimates to a series.
            self.estimate_series.append(earnings_date_time, row['EPS Estimate'])

            # if the estimate was missed add it to the missed series
            if row['Reported EPS'] < row['EPS Estimate']:
                self.missed_series.append(earnings_date_time, row['Reported EPS'])
            # otherwise add it to the achieved series
            else:
                self.achieved_series.append(earnings_date_time, row['Reported EPS'])

        print(self.missed_series.points())

        self.chart = QChart()
        self.chart.legend().hide()

        self.chart.addAxis(self.axisY, Qt.AlignLeft)
        self.chart.addAxis(self.axisX, Qt.AlignBottom)

        self.chart.addSeries(self.estimate_series)
        self.estimate_series.attachAxis(self.axisY)
        self.estimate_series.attachAxis(self.axisY)

        self.chart.addSeries(self.missed_series)
        self.missed_series.attachAxis(self.axisY)
        self.missed_series.attachAxis(self.axisY)

        self.chart.addSeries(self.achieved_series)
        self.achieved_series.attachAxis(self.axisY)
        self.achieved_series.attachAxis(self.axisY)

        self.chart.setTitle("Earnings Per Share (estimated versus actual)")

        self._chart_view = QChartView(self.chart)
        self._chart_view.setRenderHint(QPainter.Antialiasing)

        self.setCentralWidget(self._chart_view)

if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = TestChart()
    window.show()
    window.resize(1000, 1000)
    sys.exit(app.exec())
问题原因
  • X轴未与系列绑定:代码中仅给三个系列绑定了Y轴(还重复绑定了两次),但完全没有绑定X轴。QChart的自定义轴必须和系列关联,否则系列会自动计算自身数据范围来适配图表,导致你手动设置的X轴边距完全无效。
  • 重复绑定Y轴:每个系列都执行了两次attachAxis(self.axisY),属于冗余操作,没有实际作用。
  • 日期转换潜在问题:若pandas的datetime索引包含时区信息,转换为QDateTime时可能导致日期显示错误,需确保索引为无时区的datetime类型。
修复方案

修改系列绑定轴的代码,给每个系列同时绑定X轴和Y轴,去掉重复的Y轴绑定:

# 替换原有的系列绑定逻辑
self.chart.addSeries(self.estimate_series)
self.estimate_series.attachAxis(self.axisY)
self.estimate_series.attachAxis(self.axisX)  # 新增X轴绑定

self.chart.addSeries(self.missed_series)
self.missed_series.attachAxis(self.axisY)
self.missed_series.attachAxis(self.axisX)  # 新增X轴绑定

self.chart.addSeries(self.achieved_series)
self.achieved_series.attachAxis(self.axisY)
self.achieved_series.attachAxis(self.axisX)  # 新增X轴绑定

另外,若原数据的datetime索引带有时区,建议添加时区清除处理:

earnings_dates_df.index = earnings_dates_df.index.tz_localize(None)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 14:02:05