matplotlib: форматировать значения смещения оси в целые числа или конкретное число

92

У меня есть фигура matplotlib, на которой я рисую данные, которые всегда называются наносекундами (1e-9). По оси ординат, если у меня есть данные в десятки наносекунд, т.е. 44e-9, значение на оси отображается как 4.4 с + 1e-8 в качестве смещения. Есть ли способ заставить ось показывать 44 со смещением + 1e-9?

То же самое касается моей оси x, где ось показывает + 5.54478e4, где я бы предпочел показать смещение +55447 (целое число, без десятичного числа - здесь значение в днях).

Я пробовал пару вещей вроде этого:

p = axes.plot(x,y)
p.ticklabel_format(style='plain')

для оси x, но это не работает, хотя я, вероятно, использую его неправильно или неправильно интерпретирую что-то из документации, может ли кто-нибудь указать мне правильное направление?

Спасибо, Джонатан

Иллюстрация проблемы


Я пробовал что-то делать с форматерами, но пока не нашел решения ...:

myyfmt = ScalarFormatter(useOffset=True)
myyfmt._set_offset(1e9)
axes.get_yaxis().set_major_formatter(myyfmt)

а также

myxfmt = ScalarFormatter(useOffset=True)
myxfmt.set_portlimits((-9,5))
axes.get_xaxis().set_major_formatter(myxfmt)

Кстати, я действительно не понимаю, где на самом деле находится объект «номер смещения» ... это часть основных / второстепенных тиков?

Джонатан
источник
1
Вы пробовали set_units? matplotlib.sourceforge.net/api/… (Я не могу попробовать, потому что у меня нет здесь matplotlib.)
Катриэль
1
Я проверил функцию set_units, и она кажется более сложной, чем необходимо (нужно написать / добавить дополнительный модуль ?? - basic_units?). Должен быть способ просто отредактировать формат галочки. Функция units / set_unit больше похожа на преобразование единиц измерения. Спасибо за совет, он привел меня к другим решениям, которые я ищу сейчас!
Джонатан,
1
пожалуйста, подумайте rcParamsо том, чтобы отключить его по умолчанию: rcParams["axes.formatter.useoffset"] = Falseкак здесь: stackoverflow.com/questions/24171064/…
Руджеро Турра

Ответы:

100

У меня была точно такая же проблема, и эти строки устранили ее:

from matplotlib.ticker import ScalarFormatter

y_formatter = ScalarFormatter(useOffset=False)
ax.yaxis.set_major_formatter(y_formatter)
Гонсало
источник
1
Это быстрый и простой ответ. Спасибо.
maxm
6
ax.get_yaxis().get_major_formatter().set_useOffset(False)
Однострочным текстом
3
для таких новичков, как я, не забывайте, что from matplotlib.ticker import ScalarFormatterкод @Gonzalo работает, или просто используйте решение
@Dataman
36

Гораздо более простое решение - просто настроить метки в виде галочки. Вот пример:

from pylab import *

# Generate some random data...
x = linspace(55478, 55486, 100)
y = random(100) - 0.5
y = cumsum(y)
y -= y.min()
y *= 1e-8

# plot
plot(x,y)

# xticks
locs,labels = xticks()
xticks(locs, map(lambda x: "%g" % x, locs))

# ytikcs
locs,labels = yticks()
yticks(locs, map(lambda x: "%.1f" % x, locs*1e9))
ylabel('microseconds (1E-9)')

show()

альтернативный текст

Обратите внимание, как в случае оси Y я умножил значения, а 1e9затем упомянул эту константу в метке Y


РЕДАКТИРОВАТЬ

Другой вариант - подделать множитель экспоненты, вручную добавив его текст в верхнюю часть графика:

locs,labels = yticks()
yticks(locs, map(lambda x: "%.1f" % x, locs*1e9))
text(0.0, 1.01, '1e-9', fontsize=10, transform = gca().transAxes)

РЕДАКТИРОВАТЬ2

Также вы можете отформатировать значение смещения оси x таким же образом:

locs,labels = xticks()
xticks(locs, map(lambda x: "%g" % x, locs-min(locs)))
text(0.92, -0.07, "+%g" % min(locs), fontsize=10, transform = gca().transAxes)

альтернативный текст

Амро
источник
Поначалу я именно так и поступил. К сожалению, я не смог найти простой способ установить / показать множитель оси (кроме того, что явно поместил его в метку оси Y, как это сделали вы). Если вы не против не иметь метки множителя оси, это более простой способ. В любом случае, +1 от меня.
Джо Кингтон,
1
@Joe Kington: вы можете добавить его вручную в виде текста ... см. Правку выше :)
Амро,
Большой! Я собираюсь попробовать ваш подход с метками для оси x. Я возьму нижнее значение первого значения x, затем удалю его из каждого значения x и добавлю "+ minxval" в качестве метки. Я не могу понять, как еще отформатировать смещение x-tick. Меня устраивает величина смещения, мне просто нужно, чтобы она отображалась как неэкспоненциальное значение.
Джонатан
Вау. Отличная работа в демонстрации того, как вы действительно можете взять под контроль matplotlib и настроить его под свои нужды, а также немного изумления для ваших сюжетов.
Physicsmichael
Как изменить размер шрифта 1e-9 на рисунке?
от предложения нельзя отказаться
30

Вы должны создать подкласс, ScalarFormatterчтобы делать то, что вам нужно ... _set_offsetпросто добавляет константу, которую вы хотите установить ScalarFormatter.orderOfMagnitude. К сожалению, ручная установка orderOfMagnitudeничего не даст, так как она сбрасывается, когда ScalarFormatterэкземпляр вызывается для форматирования меток отметок оси. Это не должно быть так сложно, но я не могу найти более простого способа сделать именно то, что вы хотите ... Вот пример:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter, FormatStrFormatter

class FixedOrderFormatter(ScalarFormatter):
    """Formats axis ticks using scientific notation with a constant order of 
    magnitude"""
    def __init__(self, order_of_mag=0, useOffset=True, useMathText=False):
        self._order_of_mag = order_of_mag
        ScalarFormatter.__init__(self, useOffset=useOffset, 
                                 useMathText=useMathText)
    def _set_orderOfMagnitude(self, range):
        """Over-riding this to avoid having orderOfMagnitude reset elsewhere"""
        self.orderOfMagnitude = self._order_of_mag

# Generate some random data...
x = np.linspace(55478, 55486, 100) 
y = np.random.random(100) - 0.5
y = np.cumsum(y)
y -= y.min()
y *= 1e-8

# Plot the data...
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'b-')

# Force the y-axis ticks to use 1e-9 as a base exponent 
ax.yaxis.set_major_formatter(FixedOrderFormatter(-9))

# Make the x-axis ticks formatted to 0 decimal places
ax.xaxis.set_major_formatter(FormatStrFormatter('%0.0f'))
plt.show()

Что дает что-то вроде: альтернативный текст

Принимая во внимание, что форматирование по умолчанию будет выглядеть так: альтернативный текст

Надеюсь, это немного поможет!

Изменить: что бы это ни стоило, я тоже не знаю, где находится метка смещения ... Было бы немного проще просто установить ее вручную, но я не мог понять, как это сделать ... Я чувствую что должен быть более простой способ, чем все это. Но это работает!

Джо Кингтон
источник
Благодарность! Создание подклассов ScalarFormatter отлично работает! Но я полагаю, что я не ясно указал, что мне нужно для оси x. Я хотел бы сохранить смещение для оси x, но отформатировать значение смещения, чтобы оно не отображалось как экспонента.
Джонатан,
Это единственный метод, который у меня сработал! Спасибо :)
Ocean Scientist
11

Подобно ответу Амро, вы можете использовать FuncFormatter

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter

# Generate some random data...
x = np.linspace(55478, 55486, 100) 
y = np.random.random(100) - 0.5
y = np.cumsum(y)
y -= y.min()
y *= 1e-8

# Plot the data...
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'b-')

# Force the y-axis ticks to use 1e-9 as a base exponent 
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: ('%.1f')%(x*1e9)))
ax.set_ylabel('microseconds (1E-9)')

# Make the x-axis ticks formatted to 0 decimal places
ax.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: '%.0f'%x))
plt.show()
idrinkpabst
источник
5

Решение Gonzalo начало работать у меня после добавления set_scientific(False):

ax=gca()
fmt=matplotlib.ticker.ScalarFormatter(useOffset=False)
fmt.set_scientific(False)
ax.xaxis.set_major_formatter(fmt)
ha7ilm
источник
5

Как было указано в комментариях и в этом ответе , смещение можно отключить глобально, выполнив следующие действия:

matplotlib.rcParams['axes.formatter.useoffset'] = False
Евгений Сергеев
источник
4

Я думаю, что более элегантным способом является использование средства форматирования тикера. Вот пример для оси x и оси y:

from pylab import *
from matplotlib.ticker import MultipleLocator, FormatStrFormatter

majorLocator   = MultipleLocator(20)
xFormatter = FormatStrFormatter('%d')
yFormatter = FormatStrFormatter('%.2f')
minorLocator   = MultipleLocator(5)


t = arange(0.0, 100.0, 0.1)
s = sin(0.1*pi*t)*exp(-t*0.01)

ax = subplot(111)
plot(t,s)

ax.xaxis.set_major_locator(majorLocator)
ax.xaxis.set_major_formatter(xFormatter)
ax.yaxis.set_major_formatter(yFormatter)

#for the minor ticks, use no labels; default NullFormatter
ax.xaxis.set_minor_locator(minorLocator)
Богдан
источник
1
Это не отвечает на вопрос, как указать смещение и / или коэффициент, используемый в экспоненциальном представлении .
sodd 07
@nordev Даже если мой ответ не отвечает конкретно на вопрос, он все же дает намек. Сообщение состоит в том, что вы можете выбрать другое средство форматирования и получить желаемое вместо даты из моего примера. В научном мире юлианский день является нормой, или вы можете использовать дату, как в моем примере. Я пытался предложить другой подход. Иногда вопрос может быть задан, потому что человек в данный момент не имеет лучшего представления. Нельзя отказываться от альтернативных решений или относиться к ним неуважительно. В общем, я не заслужил «-1».
Богдан
2

Для второй части, без повторного сброса всех тиков вручную, это было мое решение:

class CustomScalarFormatter(ScalarFormatter):
    def format_data(self, value):
        if self._useLocale:
            s = locale.format_string('%1.2g', (value,))
        else:
            s = '%1.2g' % value
        s = self._formatSciNotation(s)
        return self.fix_minus(s)
xmajorformatter = CustomScalarFormatter()  # default useOffset=True
axes.get_xaxis().set_major_formatter(xmajorformatter)

очевидно, вы можете установить в строке формата все, что захотите.

Astrojuanlu
источник
К сожалению, я не исследовал, как установить множитель, как указано в первой части вашего вопроса.
astrojuanlu