Alterando a “frequência de ticks” no eixo x ou y no matplotlib?

477

Estou tentando corrigir como python plota meus dados.

Dizer

x = [0,5,9,10,15]

e

y = [0,1,2,3,4]

Então eu faria:

matplotlib.pyplot.plot(x,y)
matplotlib.pyplot.show()

e os ticks do eixo x são plotados em intervalos de 5. Existe uma maneira de mostrar intervalos de 1?

Dax Feliz
fonte
6
Embora ticks seja a palavra apropriada aqui, alterar os ticks para o tamanho da etapa definitivamente guiará mais novatos a essa pergunta.
Sibbs Gambling
9
Questão intimamente relacionados: stackoverflow.com/questions/6682784/... e uma grande solução:pyplot.locator_params(nbins=4)
Dr. Jan-Philip Gehrcke
nbins parece ter sido preterido em matplotlib2.x, infelizmente
jeremy_rutman

Respostas:

583

Você pode definir explicitamente onde deseja marcar as marcas plt.xticks:

plt.xticks(np.arange(min(x), max(x)+1, 1.0))

Por exemplo,

import numpy as np
import matplotlib.pyplot as plt

x = [0,5,9,10,15]
y = [0,1,2,3,4]
plt.plot(x,y)
plt.xticks(np.arange(min(x), max(x)+1, 1.0))
plt.show()

( np.arangefoi usada em vez da rangefunção do Python apenas por precaução min(x)e max(x)são flutuadores em vez de ints.)


A função plt.plot(ou ax.plot) definirá automaticamente o padrão xe os ylimites. Se você deseja manter esses limites e apenas alterar o tamanho das etapas das marcas de escala, poderá ax.get_xlim()descobrir quais limites o Matplotlib já definiu.

start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, stepsize))

O formatador de ticks padrão deve fazer um trabalho decente, arredondando os valores de ticks para um número razoável de dígitos significativos. No entanto, se você deseja ter mais controle sobre o formato, pode definir seu próprio formatador. Por exemplo,

ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))

Aqui está um exemplo executável:

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

x = [0,5,9,10,15]
y = [0,1,2,3,4]
fig, ax = plt.subplots()
ax.plot(x,y)
start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, 0.712123))
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
plt.show()
unutbu
fonte
72
Não há como fazê-lo ainda decidir seus próprios limites, mas apenas alterar o tamanho da etapa? Este método não é muito bom se o min é algo como 3523.232512!
Korone
3
@ Corone, já faz um tempo desde que você perguntou, mas eu postei uma resposta abaixo que permite o controle fácil do tamanho da etapa enquanto ainda se usa a determinação automática de limites.
jthomas
3
Note que o +1no plt.xticks(np.arange(min(x), max(x)+1, 1.0))é necessário para mostrar a última marca de escala.
Alex Willison #
1
Sim, np.arange(start, stop)gera valores no intervalo semiaberto[start, stop) , incluindo startmas excluindo stop. Então, eu costumava max(x)+1garantir que isso max(x)está incluído.
Unutbu 19/05
4
existe um equivalente para datetime, por exemplo plt.xticks(np.arange(min(dates), max(dates)+0.1,0.1)? parece traçar apenas o ano
WBM
207

Outra abordagem é definir o localizador de eixos:

import matplotlib.ticker as plticker

loc = plticker.MultipleLocator(base=1.0) # this locator puts ticks at regular intervals
ax.xaxis.set_major_locator(loc)

Existem vários tipos diferentes de localizador, dependendo de suas necessidades.

Aqui está um exemplo completo:

import matplotlib.pyplot as plt
import matplotlib.ticker as plticker

x = [0,5,9,10,15]
y = [0,1,2,3,4]
fig, ax = plt.subplots()
ax.plot(x,y)
loc = plticker.MultipleLocator(base=1.0) # this locator puts ticks at regular intervals
ax.xaxis.set_major_locator(loc)
plt.show()
robochat
fonte
7
Isso não funciona conforme o esperado. Especificamente, ao usar datas, ele não usa as datas apropriadas.
precisa saber é o seguinte
35
Ao usar datas, você deve usar os métodos no módulo matplotlib.dates. Por exemplomatplotlib.dates.AutoDateLocator()
robochat
3
Funcionou como esperado para mim, com datas. Esta solução é muito mais fácil que a aceita.
Pablo Suau
O que base=1.0realmente significa / faz?
javadba 23/04
base = 1.0 significa que haverá um localizador para cada número inteiro. A documentação diz que o MultipleLocator "Define [s] uma marca em cada múltiplo inteiro de uma base dentro do intervalo de exibição". Portanto, se base = 2, haverá uma marca para números pares e acho que você poderia colocar base = 2,5.
robochat 29/04
124

Gosto desta solução (do Matplotlib Plotting Cookbook ):

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

x = [0,5,9,10,15]
y = [0,1,2,3,4]

tick_spacing = 1

fig, ax = plt.subplots(1,1)
ax.plot(x,y)
ax.xaxis.set_major_locator(ticker.MultipleLocator(tick_spacing))
plt.show()

Esta solução fornece controle explícito do espaçamento dos ticks através do número indicado ticker.MultipleLocater(), permite a determinação automática de limites e é fácil de ler posteriormente.

jthomas
fonte
3
Uma maneira de fazer isso sem calcular explicitamente os ticks!
Zelphir Kaltstahl
4
Esta é a mesma resposta que esta . Não faz sentido adicionar uma resposta idêntica dois anos depois.
ImportanceOfBeingErnest
6
Boa pegada. Não os reconheci da mesma forma quando publiquei a resposta. Ainda assim, acho que essa apresentação é um pouco mais fácil de entender.
Jthomas
A referência do livro nesta resposta também fornece uma fonte útil para mais informações.
Steven C. Howell
1
Esta é a mesma resposta que a do robochat, que veio três anos antes.
MERose 8/09/19
90

Caso alguém esteja interessado em uma única linha geral, basta obter os ticks atuais e usá-lo para definir os novos ticks, amostrando todos os outros ticks.

ax.set_xticks(ax.get_xticks()[::2])
glopes
fonte
3
Esta é a resposta só generalizável para diferentes tipos de carrapatos (str, flutuador, de data e hora)
Ryszard Cetnarski
2
Remover ticks não inteiros: ax.set_xticks([tick for tick in ax.get_xticks() if tick % 1 == 0])
user2839288
Muitas soluções detalhadas acima, mas eu concordo que essa é a mais concisa. Você pode até extrair o comprimento de ax.get_xticks () e definir a frequência de corte por esse comprimento dividido pelo número de ticks necessários.
Iain D
Eu acho que essa é a melhor resposta. A maioria das outras respostas é muito complicada e difícil de aplicar / generalizar. Obrigado!
Seankala 01/09/19
2
Ele só pode reduzir o número de paus, enquanto a questão (e meu objetivo, como eu o encontrei) era aumentá-lo.
Alexei Martianov
36

Isso é um pouco hacky, mas de longe o exemplo mais limpo / fácil de entender que eu encontrei para fazer isso. É de uma resposta no SO aqui:

A maneira mais limpa de ocultar todos os enésimos rótulos de marcadores na barra de cores matplotlib?

for label in ax.get_xticklabels()[::2]:
    label.set_visible(False)

Em seguida, você pode fazer um loop sobre os rótulos, definindo-os como visíveis ou não, dependendo da densidade desejada.

edit: note que, às vezes, o matplotlib define rótulos == '', portanto, pode parecer que um rótulo não está presente, quando na verdade está e simplesmente não está exibindo nada. Para garantir que você esteja percorrendo rótulos visíveis reais, tente:

visible_labels = [lab for lab in ax.get_xticklabels() if lab.get_visible() is True and lab.get_text() != '']
plt.setp(visible_labels[::2], visible=False)
choldgraf
fonte
3
Esta é a solução mais simples e genérica. Um pequeno ajuste: geralmente ax.get_xticklabels()[1::2]os rótulos ficam ocultos.
jolvi
Isso não funciona com matplotlib.finance.candlestick2
BCR
@BCR, pode ser que alguns dos xticklabels estejam configurados para ''que, quando você os percorra, os xticklabels estejam vazios invisíveis (o que não teria efeito na visualização, mas pode significar que você não está puxando os rótulos corretos). Você pode tentar: vis_labels = [label for label in ax.get_xticklabels() if label.get_visible() is True]; plt.setp(vis_labels[::2], visible==False)
choldgraf 15/02
15

Este é um tópico antigo, mas tropeço nele de vez em quando e faço essa função. É muito conveniente:

import matplotlib.pyplot as pp
import numpy as np

def resadjust(ax, xres=None, yres=None):
    """
    Send in an axis and I fix the resolution as desired.
    """

    if xres:
        start, stop = ax.get_xlim()
        ticks = np.arange(start, stop + xres, xres)
        ax.set_xticks(ticks)
    if yres:
        start, stop = ax.get_ylim()
        ticks = np.arange(start, stop + yres, yres)
        ax.set_yticks(ticks)

Uma ressalva de controlar os ticks dessa maneira é que não se desfruta mais da atualização automagic interativa da escala máxima após uma linha adicionada. Então faça

gca().set_ylim(top=new_top) # for example

e execute a função de reajuste novamente.

Tompa
fonte
11

Eu desenvolvi uma solução deselegante. Considere que temos o eixo X e também uma lista de rótulos para cada ponto em X.

Exemplo:
import matplotlib.pyplot as plt

x = [0,1,2,3,4,5]
y = [10,20,15,18,7,19]
xlabels = ['jan','feb','mar','apr','may','jun']
Digamos que eu queira mostrar marcadores de ticks apenas para 'feb' e 'jun'
xlabelsnew = []
for i in xlabels:
    if i not in ['feb','jun']:
        i = ' '
        xlabelsnew.append(i)
    else:
        xlabelsnew.append(i)
Bom, agora temos uma lista falsa de etiquetas. Primeiro, plotamos a versão original.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabels,rotation=45)
plt.show()
Agora, a versão modificada.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabelsnew,rotation=45)
plt.show()
Deninhos
fonte
6

se você quiser apenas definir o espaçamento como um liner simples, com um mínimo de clichê:

plt.gca().xaxis.set_major_locator(plt.MultipleLocator(1))

também funciona facilmente para pequenos ticks:

plt.gca().xaxis.set_minor_locator(plt.MultipleLocator(1))

um pouco de boca cheia, mas bastante compacto

Gary Steele
fonte
2
xmarks=[i for i in range(1,length+1,1)]

plt.xticks(xmarks)

Isso funcionou para mim

se você quiser carrapatos entre [1,5] (1 e 5 inclusive), substitua

length = 5
Piyush Gupta
fonte
1
fyi, você poderia simplesmente escrever xmarks = range(1, length+1, 1). tenho certeza que a compreensão da lista é redundante.
Neal
2

Implementação Pure Python

Abaixo está uma implementação python pura da funcionalidade desejada que lida com qualquer série numérica (int ou float) com valores positivos, negativos ou mistos e permite ao usuário especificar o tamanho da etapa desejada:

import math

def computeTicks (x, step = 5):
    """
    Computes domain with given step encompassing series x
    @ params
    x    - Required - A list-like object of integers or floats
    step - Optional - Tick frequency
    """
    xMax, xMin = math.ceil(max(x)), math.floor(min(x))
    dMax, dMin = xMax + abs((xMax % step) - step) + (step if (xMax % step != 0) else 0), xMin - abs((xMin % step))
    return range(dMin, dMax, step)

Saída de amostra

# Negative to Positive
series = [-2, 18, 24, 29, 43]
print(list(computeTicks(series)))

[-5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45]

# Negative to 0
series = [-30, -14, -10, -9, -3, 0]
print(list(computeTicks(series)))

[-30, -25, -20, -15, -10, -5, 0]

# 0 to Positive
series = [19, 23, 24, 27]
print(list(computeTicks(series)))

[15, 20, 25, 30]

# Floats
series = [1.8, 12.0, 21.2]
print(list(computeTicks(series)))

[0, 5, 10, 15, 20, 25]

# Step – 100
series = [118.3, 293.2, 768.1]
print(list(computeTicks(series, step = 100)))

[100, 200, 300, 400, 500, 600, 700, 800]

Uso da amostra

import matplotlib.pyplot as plt

x = [0,5,9,10,15]
y = [0,1,2,3,4]
plt.plot(x,y)
plt.xticks(computeTicks(x))
plt.show()

Gráfico de uso da amostra

Observe que o eixo x tem valores inteiros todos uniformemente espaçados por 5, enquanto o eixo y tem um intervalo diferente (o matplotlibcomportamento padrão, porque os ticks não foram especificados).

Greenstick
fonte