¿Cómo mover el factor de escala del eje y a la posición próxima a la etiqueta del eje y?

Tengo algunos datos graficados que fuerzo a la notación científica a potencias de 10 (en lugar de exponencial). Aquí hay un fragmento del código:

import matplotlib.ticker as mticker formatter = mticker.ScalarFormatter(useMathText=True) formatter.set_powerlimits((-3,2)) ax.yaxis.set_major_formatter(formatter) 

Sin embargo, el factor de escala de x10 ^ -4 aparece en la esquina superior izquierda de la gráfica.

¿Existe un método simple para forzar la posición de este factor de escala junto a la etiqueta y como se ilustra en el diagtwig a continuación?

introduzca la descripción de la imagen aquí

Puede configurar el desplazamiento como invisible, de manera que no aparezca en su posición original.

 ax.yaxis.offsetText.set_visible(False) 

A continuación, puede obtener el desplazamiento del formateador y actualizar la etiqueta con él.

 offset = ax.yaxis.get_major_formatter().get_offset() ax.yaxis.set_label_text("original label" + " " + offset) 

tal que aparece dentro de la etiqueta.

Lo siguiente automatiza esto utilizando una clase con una callback, de modo que si el desplazamiento cambia, se actualizará en la etiqueta.

 import numpy as np import matplotlib.pyplot as plt import matplotlib.ticker as mticker class Labeloffset(): def __init__(self, ax, label="", axis="y"): self.axis = {"y":ax.yaxis, "x":ax.xaxis}[axis] self.label=label ax.callbacks.connect(axis+'lim_changed', self.update) ax.figure.canvas.draw() self.update(None) def update(self, lim): fmt = self.axis.get_major_formatter() self.axis.offsetText.set_visible(False) self.axis.set_label_text(self.label + " "+ fmt.get_offset() ) x = np.arange(5) y = np.exp(x)*1e-6 fig, ax = plt.subplots() ax.plot(x,y, marker="d") formatter = mticker.ScalarFormatter(useMathText=True) formatter.set_powerlimits((-3,2)) ax.yaxis.set_major_formatter(formatter) lo = Labeloffset(ax, label="my label", axis="y") plt.show() 

introduzca la descripción de la imagen aquí

Ejemplo mínimo para una GUI, donde solo se debe draw() una vez por actualización de trazado. Mantiene el factor de escala correcto. También se puede utilizar con exponente bloqueado, por ejemplo, como aquí .

Como ejemplo, acabo de agregar un simple botón para actualizar la ttwig, pero podría ser cualquier otro evento.

 from PyQt5.Qt import * from PyQt5 import QtWidgets, QtCore # sorry about the Qt Creator mess from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure from matplotlib.ticker import ScalarFormatter import numpy as np class WidgetPlot(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setLayout(QVBoxLayout()) self.canvas = PlotCanvas(self) self.layout().addWidget(self.canvas) class PlotCanvas(FigureCanvas): def __init__(self, parent = None, width = 5, height = 5, dpi = 100): self.fig = Figure(figsize = (width, height), dpi = dpi, tight_layout = True) self.ax = self.fig.add_subplot(111) FigureCanvas.__init__(self, self.fig) def majorFormatterInLabel(self, ax, axis, axis_label, major_formatter): if axis == "x": axis = ax.xaxis if axis == "y": axis = ax.yaxis axis.set_major_formatter(major_formatter) axis.offsetText.set_visible(False) exponent = axis.get_offset_text().get_text() axis.set_label_text(axis_label + " (" + exponent + ")") class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(674, 371) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget) self.gridLayoutWidget.setGeometry(QtCore.QRect(50, 10, 601, 281)) self.gridLayoutWidget.setObjectName("gridLayoutWidget") self.mpl_layoutBox = QtWidgets.QGridLayout(self.gridLayoutWidget) self.mpl_layoutBox.setContentsMargins(0, 0, 0, 0) self.mpl_layoutBox.setObjectName("mpl_layoutBox") self.pushButton = QtWidgets.QPushButton(self.centralwidget) self.pushButton.setGeometry(QtCore.QRect(280, 300, 113, 32)) self.pushButton.setObjectName("pushButton") MainWindow.setCentralWidget(self.centralwidget) self.w = WidgetPlot() self.canvas = self.w.canvas self.mpl_layoutBox.addWidget(self.w) self.pushButton.clicked.connect(self.refresh) def refresh(self): self.canvas.ax.clear() # Could've made it more beautiful. In any case, the cost of doing this is sub-ms. formatter = ScalarFormatter() formatter.set_powerlimits((-1, 1)) self.canvas.majorFormatterInLabel(self.canvas.ax, "y", "label", major_formatter = formatter) r = np.random.choice((1e3, 1e5, 1e7, 1e9, 1e100)) self.canvas.ax.plot(r) self.canvas.draw() if __name__ == "__main__": import sys app = QApplication(sys.argv) TraceWindow = QMainWindow() ui = Ui_MainWindow() ui.setupUi(TraceWindow) TraceWindow.show() sys.exit(app.exec_())