matplotlib: formata os valores de deslocamento do eixo para números inteiros ou número específico

92

Eu tenho uma figura matplotlib que estou traçando dados que sempre é referido como nanossegundos (1e-9). No eixo y, se eu tiver dados que são dezenas de nanossegundos, ou seja, 44e-9, o valor no eixo mostra 4,4 com a + 1e-8 como um deslocamento. Existe alguma maneira de forçar o eixo a mostrar 44 com um deslocamento + 1e-9?

O mesmo vale para meu eixo x, onde o eixo está mostrando + 5,54478e4, onde prefiro que ele mostre um deslocamento de +55447 (número inteiro, sem decimal - o valor aqui é em dias).

Eu tentei algumas coisas como esta:

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

para o eixo x, mas isso não funciona, embora eu provavelmente esteja usando incorretamente ou interpretando mal algo dos documentos, alguém pode me apontar a direção correta?

Obrigado Jonathan

Ilustração do problema


Tentei fazer algo com formatadores, mas ainda não encontrei nenhuma solução ...:

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

e

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

Em uma nota lateral, estou realmente confuso sobre onde o objeto 'número de deslocamento' realmente reside ... é parte dos tiques principais / secundários?

Jonathan
fonte
1
Você já tentou set_units? matplotlib.sourceforge.net/api/… (Não posso tentar porque não tenho matplotlib aqui.)
Katriel,
1
Eu verifiquei a função set_units e parece muito mais complicado do que o necessário (tem que escrever / adicionar um módulo adicional ?? - basic_units?). Deve haver uma maneira de apenas editar o formato do tique. A função units / set_unit parece estar mais voltada para a conversão de unidades. Obrigado pela dica, porém, ela me levou a algumas outras soluções que estou procurando agora!
Jonathan,
1
considere rcParamsdesativá-lo por padrão: rcParams["axes.formatter.useoffset"] = Falsecomo aqui: stackoverflow.com/questions/24171064/…
Ruggero Turra

Respostas:

100

Eu tive exatamente o mesmo problema, e estas linhas resolveram o problema:

from matplotlib.ticker import ScalarFormatter

y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
ax.yaxis.set_major_formatter(y_formatter)
Gonzalo
fonte
1
Esta é a resposta rápida e fácil. Obrigado.
máx.
6
Um one-liner seria:ax.get_yaxis().get_major_formatter().set_useOffset(False)
Dataman
3
para novatos como eu, não esqueça o from matplotlib.ticker import ScalarFormattercódigo do @Gonzalo para funcionar ou simplesmente use a solução de
@Dataman
36

Uma solução muito mais fácil é simplesmente personalizar os rótulos dos carrapatos. Veja este exemplo:

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()

texto alternativo

Observe como, no caso do eixo y, eu multipliquei os valores 1e9e mencionei essa constante no rótulo y


EDITAR

Outra opção é falsificar o multiplicador de expoentes adicionando manualmente seu texto no topo do gráfico:

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

EDIT2

Além disso, você pode formatar o valor de deslocamento do eixo x da mesma maneira:

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)

texto alternativo

Amro
fonte
Isso foi exatamente o que eu fiz, no início. Infelizmente, não consegui encontrar uma maneira fácil de definir / mostrar o multiplicador do eixo (além de colocá-lo explicitamente no rótulo do eixo y, como você fez). Se você não se importa em não ter o rótulo do multiplicador de eixo, esta é a maneira mais simples. De qualquer maneira, +1 de mim.
Joe Kington,
1
@Joe Kington: você pode adicioná-lo manualmente como texto ... veja a edição acima :)
Amro
Ótimo! Vou tentar sua abordagem com os rótulos do eixo x. Vou pegar a base do primeiro valor x, em seguida removê-lo de cada valor x e adicionar um "+ minxval" como um rótulo. Não consigo descobrir como formatar o deslocamento x-tick. Estou bem com a magnitude do deslocamento, só preciso que ele seja exibido como um valor não exponencial.
Jonathan
Uau. Excelente trabalho em mostrar como você pode realmente assumir o controle do matplotlib e ajustá-lo às suas necessidades e realmente e um pouco de entusiasmo nos seus enredos.
physicsmichael
Como você altera o tamanho da fonte de 1e-9 na figura?
uma oferta não pode ser
30

Você tem que criar uma subclasse ScalarFormatterpara fazer o que você precisa ... _set_offsetapenas adiciona uma constante que você deseja definir ScalarFormatter.orderOfMagnitude. Infelizmente, a configuração manual orderOfMagnitudenão fará nada, já que é redefinida quando a ScalarFormatterinstância é chamada para formatar os rótulos de escala do eixo. Não deveria ser tão complicado, mas não consigo encontrar uma maneira mais fácil de fazer exatamente o que você deseja ... Aqui está um exemplo:

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()

O que produz algo como: texto alternativo

Visto que a formatação padrão seria assim: texto alternativo

Espero que ajude um pouco!

Editar: pelo que vale a pena, também não sei onde o rótulo de deslocamento reside ... Seria um pouco mais fácil apenas configurá-lo manualmente, mas não consegui descobrir como fazer isso ... Tenho a sensação que tem que haver uma maneira mais fácil do que tudo isso. Mas funciona!

Joe Kington
fonte
Obrigado! Subclassificar o ScalarFormatter funciona muito bem! Mas acho que não declarei claramente o que queria para o eixo x. Gostaria de manter o deslocamento para o eixo x, mas formatar o valor do deslocamento para que não seja mostrado como um expoente.
Jonathan
Este é o único método que funcionou para mim! Obrigado :)
Ocean Scientist
11

Semelhante à resposta de Amro, você pode usar 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
fonte
5

A solução de Gonzalo começou a funcionar para mim depois de adicionar set_scientific(False):

ax=gca()
fmt=matplotlib.ticker.ScalarFormatter(useOffset=False)
fmt.set_scientific(False)
ax.xaxis.set_major_formatter(fmt)
ha7ilm
fonte
5

Conforme apontado nos comentários e nesta resposta , o deslocamento pode ser desativado globalmente, fazendo o seguinte:

matplotlib.rcParams['axes.formatter.useoffset'] = False
Evgeni Sergeev
fonte
4

Acho que uma forma mais elegante é usar o formatador de ticker. Aqui está um exemplo para eixo xis e eixo 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)
Bogdan
fonte
1
Isso não responde à questão, que é como especificar o deslocamento e / ou o fator usado na notação científica .
sodd
@nordev Mesmo que minha resposta não responda especificamente à pergunta, ela ainda dá uma dica. A mensagem é que você pode escolher outro formatador e obter o que deseja em vez de uma data do meu exemplo. No mundo científico, o dia juliano é a norma, ou você pode usar a data como no meu exemplo. O que eu estava tentando sugerir é que uma abordagem diferente pode ser adotada. Às vezes, uma pergunta pode ser feita porque a pessoa não tem uma ideia melhor no momento. Soluções alternativas não devem ser descartadas ou tratadas com desrespeito. Apesar de tudo, não merecia o voto -1.
Bogdan,
2

Para a segunda parte, sem redefinir manualmente todos os ticks novamente, esta foi a minha solução:

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)

obviamente, você pode definir a string de formato para o que quiser.

astrojuanlu
fonte
Infelizmente, não investiguei como definir o multiplicador conforme afirma a primeira parte da sua pergunta.
astrojuanlu