matplotlib: memformat nilai offset sumbu ke bilangan bulat atau bilangan tertentu

92

Saya memiliki sosok matplotlib yang saya plot data yang selalu disebut sebagai nanodetik (1e-9). Pada sumbu y, jika saya memiliki data yang berukuran puluhan nanodetik, yaitu. 44e-9, nilai pada sumbu ditampilkan sebagai 4.4 dengan + 1e-8 sebagai offset. Apakah ada cara untuk memaksa sumbu menampilkan 44 dengan offset + 1e-9?

Hal yang sama berlaku untuk sumbu x saya di mana sumbu menunjukkan + 5.54478e4, di mana saya lebih suka menampilkan offset +55447 (bilangan bulat, bukan desimal - nilainya di sini dalam hitungan hari).

Saya sudah mencoba beberapa hal seperti ini:

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

untuk sumbu x, tetapi ini tidak berhasil, meskipun saya mungkin menggunakannya dengan tidak benar atau salah menafsirkan sesuatu dari dokumen, dapatkah seseorang mengarahkan saya ke arah yang benar?

Terima kasih, Jonathan

Ilustrasi masalah


Saya mencoba melakukan sesuatu dengan pemformat tetapi belum menemukan solusi apa pun ...:

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

dan

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

Di samping catatan, saya sebenarnya bingung di mana sebenarnya objek 'nomor offset' berada ... apakah itu bagian dari tanda centang mayor / minor?

Jonathan
sumber
1
Apakah Anda mencoba set_units? matplotlib.sourceforge.net/api/… (Saya tidak dapat mencobanya karena saya tidak memiliki matplotlib di sini.)
Katriel
1
Saya memeriksa fungsi set_units dan tampaknya jauh lebih rumit dari yang diperlukan (harus menulis / menambahkan modul tambahan ?? - basic_units?). Pasti ada cara untuk mengedit format centang. Fungsi units / set_unit sepertinya lebih sesuai dengan konversi unit. Terima kasih atas tipnya, ini membawa saya ke beberapa solusi lain yang saya cari sekarang!
Jonathan
1
mohon pertimbangkan rcParamsuntuk mematikan secara default: rcParams["axes.formatter.useoffset"] = Falseseperti di sini: stackoverflow.com/questions/24171064/…
Ruggero Turra

Jawaban:

100

Saya memiliki masalah yang persis sama, dan baris-baris berikut memperbaiki masalahnya:

from matplotlib.ticker import ScalarFormatter

y_formatter = ScalarFormatter(useOffset=False)
ax.yaxis.set_major_formatter(y_formatter)
Gonzalo
sumber
1
Ini adalah jawaban yang cepat dan mudah. Terima kasih.
maksm
6
Satu ax.get_yaxis().get_major_formatter().set_useOffset(False)
kalimat
3
untuk noobs seperti saya jangan lupa from matplotlib.ticker import ScalarFormatteruntuk kode @Gonzalo untuk bekerja atau cukup gunakan solusi @Dataman di atas
champost
36

Solusi yang jauh lebih mudah adalah dengan menyesuaikan label centang. Ambil contoh ini:

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

teks alt

Perhatikan bagaimana dalam kasus sumbu y, saya mengalikan nilai dengan 1e9konstanta yang disebutkan di label y


EDIT

Opsi lainnya adalah memalsukan pengganda eksponen dengan menambahkan teksnya secara manual ke bagian atas plot:

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

Anda juga dapat memformat nilai offset sumbu x dengan cara yang sama:

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)

teks alt

Amro
sumber
Itulah yang saya lakukan, pada awalnya. Sayangnya, saya tidak dapat menemukan cara mudah untuk mengatur / menampilkan pengali sumbu (selain secara eksplisit meletakkannya di label sumbu y, seperti yang Anda lakukan.). Jika Anda tidak keberatan tidak memiliki label pengali sumbu, ini adalah cara yang lebih sederhana. Bagaimanapun, +1 dari saya.
Joe Kington
1
@ Joe Kington: Anda dapat menambahkannya secara manual sebagai teks ... lihat edit di atas :)
Amro
Bagus! Saya akan mencoba pendekatan Anda dengan label untuk sumbu x. Saya akan mengambil lantai dari nilai x pertama, lalu menghapusnya dari setiap nilai x dan menambahkan "+ minxval" sebagai label. Saya tidak tahu bagaimana lagi memformat x-tick offset. Saya baik-baik saja dengan besaran offsetnya, saya hanya perlu menampilkannya sebagai nilai non-eksponensial.
Jonathan
Wow. Kerja bagus dalam menunjukkan bagaimana Anda benar-benar dapat mengendalikan matplotlib dan menyesuaikannya dengan kebutuhan Anda dan benar-benar dan beberapa pizazz untuk plot Anda.
physicsmichael
Bagaimana Anda mengubah ukuran font 1e-9 pada gambar?
penawaran tidak dapat ditolak pada
30

Anda harus membuat subkelas ScalarFormatteruntuk melakukan apa yang Anda butuhkan ... _set_offsetcukup tambahkan konstanta, yang ingin Anda tetapkan ScalarFormatter.orderOfMagnitude. Sayangnya, pengaturan manual orderOfMagnitudetidak akan melakukan apa-apa, karena disetel ulang ketika ScalarFormatterinstance dipanggil untuk memformat label centang sumbu. Seharusnya tidak serumit ini, tetapi saya tidak dapat menemukan cara yang lebih mudah untuk melakukan apa yang Anda inginkan ... Berikut contohnya:

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

Yang menghasilkan sesuatu seperti: teks alt

Padahal, format default akan terlihat seperti: teks alt

Semoga itu bisa membantu sedikit!

Sunting: Untuk apa nilainya, saya juga tidak tahu di mana label offset berada ... Akan sedikit lebih mudah untuk mengaturnya secara manual, tapi saya tidak tahu bagaimana melakukannya ... Saya mengerti bahwa pasti ada cara yang lebih mudah dari semua ini. Itu berhasil!

Joe Kington
sumber
Terima kasih! Membuat subkelas ScalarFormatter bekerja dengan baik! Tapi saya rasa saya tidak secara jelas menyatakan apa yang saya inginkan untuk sumbu x. Saya ingin menyimpan offset untuk sumbu x, tetapi memformat nilai offset sehingga tidak ditampilkan sebagai eksponen.
Jonathan
Ini adalah satu-satunya metode yang berhasil untuk saya! Terima kasih :)
Ocean Scientist
11

Mirip dengan jawaban Amro, Anda dapat menggunakan 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
sumber
5

Solusi Gonzalo mulai berfungsi untuk saya setelah menambahkan set_scientific(False):

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

Seperti yang telah ditunjukkan di komentar dan dalam jawaban ini , offset dapat dinonaktifkan secara global, dengan melakukan hal berikut:

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

Menurut saya cara yang lebih elegan adalah dengan menggunakan formatter ticker. Berikut adalah contoh untuk xaxis dan yaxis:

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
sumber
1
Ini tidak menjawab pertanyaan, yaitu bagaimana menentukan offset dan / atau faktor yang digunakan dalam notasi ilmiah .
sodd
@nordev Meskipun jawaban saya tidak secara spesifik menjawab pertanyaan itu, masih ada petunjuk. Pesannya adalah Anda dapat memilih pemformat lain dan mendapatkan apa yang Anda inginkan daripada tanggal dari contoh saya. Dalam dunia ilmiah, hari Julian adalah norma, atau Anda dapat menggunakan tanggal seperti dalam contoh saya. Apa yang saya coba sarankan adalah bahwa pendekatan yang berbeda dapat diambil. Kadang-kadang pertanyaan mungkin diajukan karena orang tersebut tidak memiliki ide yang lebih baik saat ini. Solusi alternatif tidak boleh dibuang atau diperlakukan dengan tidak hormat. Secara keseluruhan, saya tidak pantas mendapatkan -1 suara.
Bogdan
2

Untuk bagian kedua, tanpa mengatur ulang semua centang secara manual, inilah solusi saya:

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)

jelas Anda dapat mengatur string format menjadi apa pun yang Anda inginkan.

astrojuanlu
sumber
Sayangnya saya belum menyelidiki bagaimana mengatur pengali sebagai bagian pertama dari pernyataan pertanyaan Anda.
astrojuanlu