2015-07-20 20 views
6

Obecnie jeśli ustawić matplotlib y oś ticklabels do trybu naukowej daje mi wykładnik w górnej części osi y postaci 1e-5dostosować tekst wykładnik po ustawieniu limitów naukowe dotyczące matplotlib oś

chciałbym aby dostosować to do odczytu r'$\mathregular{10^{-5}}$', aby ładnie się ładowało.

Oto mój przykładowy kod:

# Create a figure and axis 
fig, ax = plt.subplots() 

# Plot 100 random points 
# the y values of which are very small 
ax.scatter(np.random.rand(100), np.random.rand(100)/100000.0) 

# Set the y limits appropriately 
ax.set_ylim(0, 1/100000.0) 

# Change the y ticklabel format to scientific format 
ax.ticklabel_format(axis='y', style='sci', scilimits=(-2, 2)) 

# Get the offset value 
offset = ax.yaxis.get_offset_text() 

# Print it out 
print '1st offset printout: {}'.format(offset) 

# Run plt.tight_layout() 
plt.tight_layout() 

# Print out offset again - you can see the value now! 
print '2nd offset printout: {}'.format(offset) 

# Change it to latex format 
offset.set_text(r'$\mathregular{10^{-5}}$') 

# Print it out 
print '3rd offset printout: {}'.format(offset) 

# Add some text to the middle of the figure just to 
# check that it isn't the latex format that's the problem 
ax.text(0.5, 0.5/100000.0, r'$\mathregular{10^{-2}}$') 

# And show the figure 
plt.show() 

moje wyjście wygląda następująco:

1st offset printout: Text(0,0.5,u'') 
2nd offset printout: Text(0,636.933,u'1e\u22125') 
3rd offset printout: Text(0,636.933,u'$\\mathregular{10^{-5}}$') 

enter image description here

Można znaleźć kod i wyjściową postać here.

Istnieją dwie osobliwości: Jedna to to, że nie mogę nadpisać wartości 1e-5 na górze osi Y (która jest celem), a drugą jest to, że muszę uruchomić plt.tight_layout(), aby zobaczyć nawet ta wartość Unicode jako przesunięcie.

Czy ktoś może mi powiedzieć, gdzie idę źle?

Dziękuję

EDIT: Oryginalny pytanie nie wyjaśnić, że chciałbym, aby automatycznie wykryć jako wykładnik jest obecnie obliczane przez ticklabel_format. Zamiast więc przekazywać ustalony ciąg do tekstu korekcji, powinien automatycznie wykryć tę wartość i odpowiednio dopasować łańcuch lateksowy.

Odpowiedz

3

W oparciu o odpowiedź @ edsmitha można wykonać jedną pracę, która ma na celu uzyskanie tekst przesunięty, przekonwertuj go na ciąg lateksu, wyłącz przesunięcie d dodaj ten ciąg w górnej części osi.

def format_exponent(ax, axis='y'): 

    # Change the ticklabel format to scientific format 
    ax.ticklabel_format(axis=axis, style='sci', scilimits=(-2, 2)) 

    # Get the appropriate axis 
    if axis == 'y': 
     ax_axis = ax.yaxis 
     x_pos = 0.0 
     y_pos = 1.0 
     horizontalalignment='left' 
     verticalalignment='bottom' 
    else: 
     ax_axis = ax.xaxis 
     x_pos = 1.0 
     y_pos = -0.05 
     horizontalalignment='right' 
     verticalalignment='top' 

    # Run plt.tight_layout() because otherwise the offset text doesn't update 
    plt.tight_layout() 
    ##### THIS IS A BUG 
    ##### Well, at least it's sub-optimal because you might not 
    ##### want to use tight_layout(). If anyone has a better way of 
    ##### ensuring the offset text is updated appropriately 
    ##### please comment! 

    # Get the offset value 
    offset = ax_axis.get_offset_text().get_text() 

    if len(offset) > 0: 
     # Get that exponent value and change it into latex format 
     minus_sign = u'\u2212' 
     expo = np.float(offset.replace(minus_sign, '-').split('e')[-1]) 
     offset_text = r'x$\mathregular{10^{%d}}$' %expo 

     # Turn off the offset text that's calculated automatically 
     ax_axis.offsetText.set_visible(False) 

     # Add in a text box at the top of the y axis 
     ax.text(x_pos, y_pos, offset_text, transform=ax.transAxes, 
       horizontalalignment=horizontalalignment, 
       verticalalignment=verticalalignment) 
    return ax 

Zauważ, że powinieneś być w stanie wykorzystać pozycję przesunięcia tekstu wywołując pos = ax_axis.get_offset_text().get_position() ale wartości te nie są w jednostkach osi (oni mogą jednostek pixel - dzięki @EdSmith - a więc nie bardzo pomocny) . Dlatego właśnie ustawiłem wartości x_pos i y_pos według dowolnej osi, na którą patrzymy.

Napisałem też małą funkcję automatycznego wykrywania odpowiednich limitów x i y (nawet jeśli wiem, że matplotlib ma wiele wymyślnych sposobów robienia tego).

def get_min_max(x, pad=0.05): 
    ''' 
    Find min and max values such that 
    all the data lies within 90% of 
    of the axis range 
    ''' 
    r = np.max(x) - np.min(x) 
    x_min = np.min(x) - pad * r 
    x_max = np.max(x) + pad * r 
    return x_min, x_max 

Tak, aby zaktualizować przykład z pytaniem (z niewielkimi zmianami, aby obie osie trzeba wykładnik):

import matplotlib.pylab as plt 
import numpy as np 

# Create a figure and axis 
fig, ax = plt.subplots() 

# Plot 100 random points that are very small 
x = np.random.rand(100)/100000.0 
y = np.random.rand(100)/100000.0 
ax.scatter(x, y) 

# Set the x and y limits 
x_min, x_max = get_min_max(x) 
ax.set_xlim(x_min, x_max) 
y_min, y_max = get_min_max(y)  
ax.set_ylim(y_min, y_max) 

# Format the exponents nicely 
ax = format_exponent(ax, axis='x') 
ax = format_exponent(ax, axis='y') 

# And show the figure 
plt.show() 

enter image description here

GIST z notebooka ipython przedstawiający wyjście kodu jest dostępne here.

Mam nadzieję, że to pomoże!

+0

Bardzo zadbane. Zakładam, że jest to błąd w matplotlib, ponieważ powinieneś być w stanie zastąpić ciąg 'offsettext' bezpośrednio. Twój opis jest nieco mylący - nie dodajesz tego ciągu, ale wyłącza "offsettext" i umieszczasz etykietę latex w tym samym miejscu. Idealnie tekst zastępujący byłby oparty na położeniu 'ax_axis.get_offset_text(). Get_position()'. Nie mogłem znaleźć sposobu na zrobienie tego, ponieważ 'pos' jest w jednostkach pikselowych (chyba), prawda? –

+0

Masz rację - teraz zmienię swoje odniesienie do "pos" w odpowiedzi. Nie mogłem wymyślić, co zrobić z tymi liczbami (w rzeczywistości - nie przyszło mi do głowy, że są to współrzędne pikseli - dobrze zgadnij!). Czy masz jakieś sugestie, w jaki sposób uzyskać wartości do aktualizacji bez wykonywania plt.tight_layout()? – KirstieJane

2

Otrzymujesz offset i ustawiasz wartość tekstową, ale wydaje się, że nie ma sposobu, aby faktycznie zastosować to do osi ... Nawet wywołanie ax.yaxis.offsetText.set_text(offset) nie aktualizuje wyświetlonego offsetu. Dzieło wokół niego usunąć przesunięcia tekstu i zastąpić uchwytów na etykiety osi,

ax.yaxis.offsetText.set_visible(False) 
ax.set_ylabel("datalabel " + r'$\left(\mathregular{10^{-5}}\right)$') 

lub zastąpienie go w polu tekstowym ręcznego, jak minimalnym przykład

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

# Create a figure and axis 
fig, ax = plt.subplots() 
mpl.rc('text', usetex = True) 

# Plot 100 random points 
# the y values of which are very small 
large = 100000.0 
x = np.random.rand(100) 
y = np.random.rand(100)/large 

ax.scatter(x,y) 

# Set the y limits appropriately 
ax.set_ylim(0, 1/large) 

# Change the y ticklabel format to scientific format 
ax.ticklabel_format(axis='y', style='sci', scilimits=(-2, 2)) 

#print(ax.yaxis.offsetText.get_position()) 
ax.yaxis.offsetText.set_visible(False) 
ax.text(-0.21, 1.01/large, r'$\mathregular{10^{-2}}$') 

# And show the figure 
plt.show() 

Znam tego ISN Idealny, ale może być, że tekst z przesunięciem nie może być zmieniony ręcznie lub może być tylko zgodny z wartościami liczbowymi ...

+0

Dzięki za odpowiedź - nie ma coś bardzo podobnego w tym [wpis na blogu] (https://alexpearce.me/2014/04/exponent-label-in-matplotlib). Jest to praca dookoła, ale pomija fakt, że metoda 'ticklabel_format' określa wykładnik dla ciebie i mam wrażenie, że powinno to być coś, co mogę uzyskać z" offsetText ". Zamierzam dostosować moje pytanie, aby spróbować uczynić to jaśniejszym. – KirstieJane

0

Dodaj dwie linie w kodzie

import matplotlib.ticker as ptick 
ax.yaxis.set_major_formatter(ptick.ScalarFormatter(useMathText=True)) 
2

Wydaje się, że plt.ticklabel_format nie działa poprawnie. Jeśli jednak zdefiniować ScalarFormatter sobie i ustawić limity dla notacji naukowej do formater, można uzyskać automatycznie przesunięte w formacie mathtext tak:

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

x = np.linspace(3,5) 
y = np.sin(np.linspace(0,6*np.pi))*1e5 

plt.plot(x,y) 

mf = matplotlib.ticker.ScalarFormatter(useMathText=True) 
mf.set_powerlimits((-2,2)) 
plt.gca().yaxis.set_major_formatter(mf) 

plt.show() 

enter image description here

Powiązane problemy