¿Cambiar la "frecuencia de tick" en el eje xo y en matplotlib?

477

Estoy tratando de arreglar cómo Python traza mis datos.

Decir

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

y

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

Entonces haría:

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

y los ticks del eje x se trazan en intervalos de 5. ¿Hay alguna manera de hacer que muestre intervalos de 1?

Dax Feliz
fuente
66
Aunque ticks es la palabra apropiada aquí, cambiar los ticks al tamaño del paso definitivamente guiará a más novatos a esta pregunta.
Sibbs Gambling
99
Pregunta estrechamente relacionada: stackoverflow.com/questions/6682784/… y una gran solución:pyplot.locator_params(nbins=4)
Dr. Jan-Philip Gehrcke
nbins parece haber quedado en desuso en matplotlib2.x, desafortunadamente
jeremy_rutman

Respuestas:

584

Puede establecer explícitamente dónde desea marcar las marcas con plt.xticks:

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

Por ejemplo,

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.arangese utilizó en lugar de la rangefunción de Python por si acaso min(x)y max(x)son flotantes en lugar de int.)


La función plt.plot(o ax.plot) establecerá automáticamente valores predeterminados xy ylímites. Si desea mantener esos límites, y simplemente cambiar el tamaño de paso de las marcas de verificación, puede usar ax.get_xlim()para descubrir qué límites ya ha establecido Matplotlib.

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

El formateador de tick predeterminado debe hacer un trabajo decente redondeando los valores de tick a un número razonable de dígitos significativos. Sin embargo, si desea tener más control sobre el formato, puede definir su propio formateador. Por ejemplo,

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

Aquí hay un ejemplo ejecutable:

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
fuente
72
¿No hay forma de lograr que todavía decida sus propios límites, pero solo cambie el tamaño del paso? ¡Este método no es muy bueno si el mínimo es algo así como 3523.232512!
Korone
3
@Corone, ha pasado un tiempo desde que preguntaste, pero he publicado una respuesta a continuación que permite un control fácil del tamaño del paso mientras sigo usando la determinación automática de límites.
jthomas
3
Tenga +1en cuenta que plt.xticks(np.arange(min(x), max(x)+1, 1.0))se requiere la entrada para mostrar la última marca de verificación.
Alex Willison
1
Sí, np.arange(start, stop)genera valores en el intervalo medio abierto[start, stop) , incluidos startpero excluyentes stop. Así que solía max(x)+1asegurarme de que max(x)esté incluido.
unutbu
44
¿hay un equivalente para datetime por ejemplo plt.xticks(np.arange(min(dates), max(dates)+0.1,0.1)? parece que solo traza el año
WBM
207

Otro enfoque es establecer el localizador de ejes:

import matplotlib.ticker as plticker

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

Existen varios tipos diferentes de localizador según sus necesidades.

Aquí hay un ejemplo 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
fuente
77
Esto no funciona como se esperaba. Específicamente, cuando usa fechas, no usa las fechas apropiadas.
Chris Fonnesbeck
35
Al usar fechas, debe usar los métodos en el módulo matplotlib.dates. Por ejemplomatplotlib.dates.AutoDateLocator()
robochat
3
Funcionó como se esperaba para mí, con fechas. Esta solución es mucho más fácil que la aceptada.
Pablo Suau
¿Qué significa base=1.0realmente / hacer?
javadba
base = 1.0 significa que habrá un localizador para cada número entero. La documentación dice que MultipleLocator "establece una marca en cada múltiplo entero de una base dentro del intervalo de visualización". Entonces, si base = 2, entonces habrá un tic para números pares y creo que podría suceder put base = 2.5.
robochat
124

Me gusta esta solución (del 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 solución le brinda un control explícito del espaciado de ticks a través del número dado ticker.MultipleLocater(), permite la determinación automática de límites y es fácil de leer más adelante.

jthomas
fuente
3
¡Una forma de hacer esto sin calcular los ticks explícitamente!
Zelphir Kaltstahl
44
Esta es la misma respuesta que esta . No tiene sentido agregar una respuesta idéntica dos años después.
ImportanceOfBeingErnest
66
Buena atrapada. No los reconocí como iguales cuando publiqué la respuesta. Aún así, creo que esta presentación es un poco más fácil de entender.
jthomas
La referencia del libro en esta respuesta también proporciona una fuente útil para obtener más información.
Steven C. Howell
1
Esta es la misma respuesta que la de robochat, que llegó tres años antes.
MERose
90

En caso de que alguien esté interesado en una línea general, simplemente obtenga los ticks actuales y úselos para configurar los nuevos ticks probando cada otro tick.

ax.set_xticks(ax.get_xticks()[::2])
glopes
fuente
3
Esta es la única respuesta generalizable para diferentes tipos de ticks (str, float, datetime)
Ryszard Cetnarski
2
Retire las garrapatas no enteros: ax.set_xticks([tick for tick in ax.get_xticks() if tick % 1 == 0])
user2839288
Muchas soluciones detalladas arriba, pero estoy de acuerdo, esta es la más concisa. Incluso podría extraer la longitud de ax.get_xticks () y establecer la frecuencia de corte por esta longitud dividida por el número de ticks requeridos.
Iain D
Creo que esta es la mejor respuesta. La mayoría de las otras respuestas son demasiado complicadas y difíciles de aplicar / generalizar. ¡Gracias!
Seankala
2
Solo puede reducir el número de barras, mientras que en la pregunta (y el objetivo de la mina cómo lo encontré) era aumentarlo.
Alexei Martianov
36

Esto es un poco hacky, pero con mucho el ejemplo más limpio / fácil de entender que he encontrado para hacer esto. Es de una respuesta en SO aquí:

¿Es la forma más limpia de ocultar cada enésima etiqueta en la barra de colores matplotlib?

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

Luego, puede recorrer las etiquetas configurándolas como visibles o no, según la densidad que desee.

editar: tenga en cuenta que a veces matplotlib establece etiquetas == '', por lo que puede parecer que una etiqueta no está presente, cuando de hecho lo está y simplemente no muestra nada. Para asegurarte de que estás recorriendo las etiquetas visibles reales, puedes intentar:

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
fuente
3
Esta es la solución más simple y genérica. Un pequeño ajuste: generalmente ax.get_xticklabels()[1::2]son las etiquetas que deben ocultarse.
jolvi
Esto no funciona con matplotlib.finance.candlestick2
BCR
@BCR podría ser que algunos de los xticklabels solo están configurados para ''que cuando los recorra , esté haciendo que xticklabels estén vacíos invisibles (lo que no tendría ningún efecto en la visualización, pero podría significar que no está tirando) las etiquetas correctas) Podrías probar: vis_labels = [label for label in ax.get_xticklabels() if label.get_visible() is True]; plt.setp(vis_labels[::2], visible==False)
choldgraf
15

Este es un tema antiguo, pero me tropiezo con esto de vez en cuando e hice esta función. Es muy 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)

Una advertencia de controlar los ticks como este es que ya no se disfruta de la actualización automática interactiva de la escala máxima después de una línea adicional. Entonces hazlo

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

y ejecute la función de reajuste nuevamente.

Tompa
fuente
11

Desarrollé una solución poco elegante. Considere que tenemos el eje X y también una lista de etiquetas para cada punto en X.

Ejemplo:
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 quiero mostrar las etiquetas de las marcas solo para 'feb' y 'jun'
xlabelsnew = []
for i in xlabels:
    if i not in ['feb','jun']:
        i = ' '
        xlabelsnew.append(i)
    else:
        xlabelsnew.append(i)
Bien, ahora tenemos una lista falsa de etiquetas. Primero, trazamos la versión original.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabels,rotation=45)
plt.show()
Ahora, la versión modificada.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabelsnew,rotation=45)
plt.show()
Deninhos
fuente
6

si solo desea establecer el espacio en un revestimiento simple con un mínimo de repetitivo:

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

También funciona fácilmente para garrapatas menores:

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

un poco bocado, pero bastante compacto

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

plt.xticks(xmarks)

Esto funciono para mi

si desea marcar entre [1,5] (1 y 5 inclusive), reemplace

length = 5
Piyush Gupta
fuente
1
para tu información, simplemente podrías escribir xmarks = range(1, length+1, 1). bastante seguro de que la comprensión de la lista es redundante.
Neal
2

Implementación de Python puro

A continuación se muestra una implementación de Python pura de la funcionalidad deseada que maneja cualquier serie numérica (int o flotante) con valores positivos, negativos o mixtos y permite al usuario especificar el tamaño de paso deseado:

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)

Salida de muestra

# 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 de muestra

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 de muestra

Observe que el eje x tiene valores enteros todos espaciados uniformemente por 5, mientras que el eje y tiene un intervalo diferente (el matplotlibcomportamiento predeterminado, porque no se especificaron los ticks).

Tallo verde
fuente