【问题标题】:Resizing Axis in Matplotlib when hiding/showing columns隐藏/显示列时在 Matplotlib 中调整轴的大小
【发布时间】:2018-01-17 14:24:31
【问题描述】:

首先,我对 Stack Overflow 和整体编码还比较陌生,所以请告诉我将来如何改进我的帖子。我目前正在开发一个应用程序,该应用程序将读取具有可变列数和范围的表格数据(在本例中为 CSV),并将所有列绘制到带有嵌入式 matplotlib 的 PyQt5 GUI 画布中。目标是能够通过单击某些内容来隐藏/显示各个列以允许快速数据比较。在我的情况下,我通过连接图例项单击事件并通过减少 alpha 将图设置为不可见来做到这一点。

这是我正在阅读的数据的花絮:

还有我的代码:

import pandas as pd
from PyQt5.QtCore import *
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QApplication, QLabel, QDialog, QLineEdit, QVBoxLayout, QComboBox, QStyleOptionComboBox, QSpinBox, QDoubleSpinBox, QGridLayout, QPushButton
from math import *
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import datetime
import sys


def main():

#Read in CSV dataset as Pandas dataframe and set index to column 0
    df = pd.read_csv('dataset.csv', index_col=0)

    #Get the count of columns in the csv file and save the headers as a list
    col_count = len(df.columns)
    col_headers = list(df)


    df['A'] = df['A']*1000

#Form class for displaying the GUI
    class Form(QDialog):

        def __init__(self, parent=None, width=400, height=400):
            super(Form, self).__init__(parent)

            #Create the figure and canvas for the plot
            self.figure = plt.figure(figsize=(width, height))
            self.canvas = FigureCanvas(self.figure)
            self.setWindowTitle("Stock Prices 1990 - 2012")

            #Create Navigation Toolbar (Possibly remove this if adding overhead and no functionality)
            self.navBar = NavigationToolbar(self.canvas, self)

            #Add Widgets to layout
            layout = QGridLayout()
            layout.addWidget(self.canvas, 2, 0)
            layout.addWidget(self.navBar, 0, 0)

            #Apply layout settings
            self.setLayout(layout)

            #Connect the pick event on the canvas to the onpick method
            self.canvas.mpl_connect('pick_event', self.onpick)

            # Add the dict as a class method so it can be passed
            self.lined = dict()

        def plot(self):
            #Create Plots and set axis labels
            plt.cla()
            ax = self.figure.add_subplot(111)
            ax.set_xlabel('Date')
            ax.set_ylabel('Price')

            #Empty list to hold the tuples of lines plotted
            lines = []

            #Set variables for each column in pandas dataframe
            for i in range(col_count):
                x, = ax.plot(pd.to_datetime(df.index), df[col_headers[i]], label=col_headers[i])
                lines.append(x)

            # Create legend from label properties
            leg = ax.legend(loc='upper left', fancybox=True, shadow=True)
            leg.get_frame().set_alpha(0.4)

            for legline, origline in zip(leg.get_lines(), lines):
                legline.set_picker(5)  # 5 pts tolerance
                self.lined[legline] = origline

            ax.autoscale(True, axis='y')

            #Draw canvas
            self.canvas.draw()

        def onpick(self, event):
            # on the pick event, find the orig line corresponding to the
            # legend proxy line, and toggle the visibility
            legline = event.artist
            origline = self.lined[legline]
            vis = not origline.get_visible()
            origline.set_visible(vis)
            # Change the alpha on the line in the legend so we can see what lines
            # have been toggled
            if vis:
                legline.set_alpha(1.0)
            else:
                legline.set_alpha(0.2)

            self.canvas.draw()

    app = QApplication(sys.argv)
    form = Form()
    form.show()
    form.plot()
    app.exec_()

if __name__ == '__main__':
    main()

图表工作的图片:

那部分工作得很好。现在的问题是我希望情节重新缩放以适应当前可见的任何内容。例如,如果我的列(y 轴)包含 10,000 - 11,000 范围内的数据,并且我隐藏该轴并显示范围为 10-20 的图,我希望 Y 轴重新缩放以适应当前显示的数据集.所以理想情况下,当我单击图例时,我希望看到图表尝试适合当前数据集。显然,在完全不同的范围内对数据集进行并排比较仍然不起作用,但我希望能够在同一个图中查看相似的数据范围,并在我更改为具有不同范围的数据集时自动切换。我试图启用自动缩放,但我猜是因为我只是将 alpha 降低到不可见,它没有重新缩放,因为绘图仍然处于活动状态。我不确定我是否应该寻找一种方法来实际删除绘图并重新绘制画布,或者可能是一种将缩放合并到我当前隐藏/显示列的方法中的方法。任何建议将不胜感激。

【问题讨论】:

  • 至于你的问题要改进什么:不要只在实际问题上花半句话。 “我希望情节重新缩放以适应当前可见的任何内容”不足以理解问题。
  • 好消息;谢谢。不知道我在发帖前怎么没有在评论中看到这一点。

标签: python-3.x pandas matplotlib anaconda pyqt5


【解决方案1】:

为了更新轴的限制,可以使用

ax.relim()
ax.autoscale_view()

但是,您是完全正确的:只要坐标区中有一条线,无论是否可见,坐标区范围都不会改变。
一种解决方案可能是从轴 (line.remove()) 中删除有问题的线,然后在单击图例 (ax.add_line(line)) 时再次添加它。在这种情况下,人们仍然可以使用该行的可见性作为在点击事件时是否应该添加或删除相关行的标志。

基于matplotlib legend picker 代码的完整示例(因为问题中的 QT 部分在这里似乎无关紧要)。

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(0.0, 1.5)
y1 = 2*np.sin(np.pi*t)
y2 = 4*np.sin(np.pi*2*t)

fig, ax = plt.subplots()
ax.set_title('Click on legend line to toggle line on/off')
line1, = ax.plot(t, y1, lw=2, color='red', label='1 HZ')
line2, = ax.plot(t, y2, lw=2, color='blue', label='2 HZ')
leg = ax.legend(loc='upper left', fancybox=True, shadow=True)
leg.get_frame().set_alpha(0.4)

lines = [line1, line2]
lined = dict()
for legline, origline in zip(leg.get_lines(), lines):
    legline.set_picker(5)  
    lined[legline] = origline


def onpick(event):
    legline = event.artist
    origline = lined[legline]

    vis = not origline.get_visible()
    origline.set_visible(vis)

    # if line is made invisible, also remove it from axes
    # if line is made visible again, add it to axes again
    if not vis:
        origline.remove()
    else:
        ax.add_line(origline)
    # in any case relimit the axes
    ax.relim()
    ax.autoscale_view()

    if vis:
        legline.set_alpha(1.0)
    else:
        legline.set_alpha(0.2)

    fig.canvas.draw_idle()

fig.canvas.mpl_connect('pick_event', onpick)

plt.show()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多