繪製日期和字串#

使用 Matplotlib 繪圖方法最基本的方式是以數值 numpy 陣列的形式傳遞座標。例如,如果 xy 是浮點數(或整數)的 numpy 陣列,則 plot(x, y) 將會有效。如果 numpy.asarray 會將 xy 轉換為浮點數陣列,繪圖方法也會有效;例如,x 可以是 python 清單。

如果資料類型存在「單位轉換器」,Matplotlib 也具有轉換其他資料類型的能力。Matplotlib 有兩個內建的轉換器,一個用於日期,另一個用於字串列表。其他下游程式庫有自己的轉換器來處理其資料類型。

將轉換器新增至 Matplotlib 的方法在 matplotlib.units 中說明。在此,我們簡要概述內建的日期和字串轉換器。

日期轉換#

如果 x 和/或 ydatetime 的清單或是 numpy.datetime64 的陣列,則 Matplotlib 具有內建轉換器,可將日期時間轉換為浮點數,並為軸新增適合日期的刻度定位器和格式器。請參閱 matplotlib.dates

在以下範例中,x 軸獲得一個轉換器,可將 numpy.datetime64 轉換為浮點數,以及一個將刻度放在每月初的定位器,和一個適當標記刻度的格式器

import numpy as np

import matplotlib.dates as mdates
import matplotlib.units as munits

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
axes units

請注意,如果我們嘗試在 x 軸上繪製浮點數,則它將以轉換器的「epoch」以來的天數為單位繪製,在此情況下為 1970-01-01 (請參閱Matplotlib 日期格式)。因此,當我們繪製值 0 時,刻度從 1970-01-01 開始。(定位器現在也選擇每兩年一個刻度,而不是每個月)

fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
# 0 gets labeled as 1970-01-01
ax.plot(0, 0, 'd')
ax.text(0, 0, ' Float x=0', rotation=45)
axes units

我們可以自訂定位器和格式器;請參閱日期刻度定位器日期格式器以取得完整清單,並參閱 日期刻度定位器和格式器 以取得它們的用法範例。在這裡,我們每隔兩個月定位一次,並僅使用月份的 3 個字母名稱使用 "%b" 設定格式 (請參閱strftime 以取得格式代碼)

fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
ax.xaxis.set_major_locator(mdates.MonthLocator(bymonth=np.arange(1, 13, 2)))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b'))
ax.set_xlabel('1980')
axes units

預設的定位器是 AutoDateLocator,預設的格式器是 AutoDateFormatter。還有提供更精簡標籤的「簡潔」格式器和定位器,並且可以透過 rcParams 設定。請注意,不是在年初使用多餘的「Jan」標籤,而是改用「1980」。請參閱使用 ConciseDateFormatter 設定日期刻度格式以取得更多範例。

plt.rcParams['date.converter'] = 'concise'

fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
axes units

我們可以透過傳遞適當的日期作為限制,或是在自 epoch 以來的天數的正確單位中傳遞浮點數值來設定軸的限制。如果我們需要,我們可以從 date2num 取得此值。

fig, axs = plt.subplots(2, 1, figsize=(5.4, 3), layout='constrained')
for ax in axs.flat:
    time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
    x = np.arange(len(time))
    ax.plot(time, x)

# set xlim using datetime64:
axs[0].set_xlim(np.datetime64('1980-02-01'), np.datetime64('1980-04-01'))

# set xlim using floats:
# Note can get from mdates.date2num(np.datetime64('1980-02-01'))
axs[1].set_xlim(3683, 3683+60)
axes units

字串轉換:分類繪圖#

有時我們想在軸上標記類別,而不是數字。Matplotlib 允許使用「類別」轉換器來執行此操作(請參閱 category)。

data = {'apple': 10, 'orange': 15, 'lemon': 5, 'lime': 20}
names = list(data.keys())
values = list(data.values())

fig, axs = plt.subplots(1, 3, figsize=(7, 3), sharey=True, layout='constrained')
axs[0].bar(names, values)
axs[1].scatter(names, values)
axs[2].plot(names, values)
fig.suptitle('Categorical Plotting')
Categorical Plotting

請注意,「類別」會按照首次指定的順序繪製,並且以不同順序進行後續繪圖不會影響原始順序。此外,新的新增項目將會新增在結尾(請參閱下面的「pear」)

fig, ax = plt.subplots(figsize=(5, 3), layout='constrained')
ax.bar(names, values)

# plot in a different order:
ax.scatter(['lemon', 'apple'], [7, 12])

# add a new category, "pear", and put the other categories in a different order:
ax.plot(['pear', 'orange', 'apple', 'lemon'], [13, 10, 7, 12], color='C1')
axes units

請注意,當使用如上所示的 plot 時,繪圖順序會對應到原始資料順序,因此新線條會按照指定的順序排列。

類別轉換器會將類別對應到從零開始的整數。因此,也可以使用浮點數手動將資料新增至軸。請注意,如果傳入一個沒有相關「類別」的浮點數,仍然可以繪製資料點,但不會建立刻度。在以下範例中,我們在 4.0 和 2.5 處繪製資料,但由於這些不是類別,因此不會在那裡新增刻度。

fig, ax = plt.subplots(figsize=(5, 3), layout='constrained')
ax.bar(names, values)
# arguments for styling the labels below:
args = {'rotation': 70, 'color': 'C1',
        'bbox': {'color': 'white', 'alpha': .7, 'boxstyle': 'round'}}


# 0 gets labeled as "apple"
ax.plot(0, 2, 'd', color='C1')
ax.text(0, 3, 'Float x=0', **args)

# 2 gets labeled as "lemon"
ax.plot(2, 2, 'd', color='C1')
ax.text(2, 3, 'Float x=2', **args)

# 4 doesn't get a label
ax.plot(4, 2, 'd', color='C1')
ax.text(4, 3, 'Float x=4', **args)

# 2.5 doesn't get a label
ax.plot(2.5, 2, 'd', color='C1')
ax.text(2.5, 3, 'Float x=2.5', **args)
axes units

可以透過指定類別或透過指定浮點數來設定類別軸的限制

fig, axs = plt.subplots(2, 1, figsize=(5, 5), layout='constrained')
ax = axs[0]
ax.bar(names, values)
ax.set_xlim('orange', 'lemon')
ax.set_xlabel('limits set with categories')
ax = axs[1]
ax.bar(names, values)
ax.set_xlim(0.5, 2.5)
ax.set_xlabel('limits set with floats')
axes units

類別軸對於某些繪圖類型很有幫助,但如果資料以字串清單的形式讀取,即使它應該是浮點數或日期的清單,也可能會導致混淆。當讀取逗號分隔值 (CSV) 檔案時,有時會發生這種情況。類別定位器和格式器會在每個字串值處放置一個刻度,並且也會標記每個刻度

fig, ax = plt.subplots(figsize=(5.4, 2.5), layout='constrained')
x = [str(xx) for xx in np.arange(100)]  # list of strings
ax.plot(x, np.arange(100))
ax.set_xlabel('x is list of strings')
axes units

如果不需要此功能,只需在繪圖前將資料轉換為浮點數即可

fig, ax = plt.subplots(figsize=(5.4, 2.5), layout='constrained')
x = np.asarray(x, dtype='float')  # array of float.
ax.plot(x, np.arange(100))
ax.set_xlabel('x is array of floats')
axes units

判斷軸上的轉換器、格式器和定位器#

有時,能夠偵錯 Matplotlib 用於轉換傳入資料的內容會很有幫助。我們可以透過查詢軸上的 converter 屬性來執行此操作。我們也可以使用 get_major_locatorget_major_formatter 來查詢格式器和定位器。

請注意,預設情況下,轉換器是

fig, axs = plt.subplots(3, 1, figsize=(6.4, 7), layout='constrained')
x = np.arange(100)
ax = axs[0]
ax.plot(x, x)
label = f'Converter: {ax.xaxis.get_converter()}\n '
label += f'Locator: {ax.xaxis.get_major_locator()}\n'
label += f'Formatter: {ax.xaxis.get_major_formatter()}\n'
ax.set_xlabel(label)

ax = axs[1]
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
label = f'Converter: {ax.xaxis.get_converter()}\n '
label += f'Locator: {ax.xaxis.get_major_locator()}\n'
label += f'Formatter: {ax.xaxis.get_major_formatter()}\n'
ax.set_xlabel(label)

ax = axs[2]
data = {'apple': 10, 'orange': 15, 'lemon': 5, 'lime': 20}
names = list(data.keys())
values = list(data.values())
ax.plot(names, values)
label = f'Converter: {ax.xaxis.get_converter()}\n '
label += f'Locator: {ax.xaxis.get_major_locator()}\n'
label += f'Formatter: {ax.xaxis.get_major_formatter()}\n'
ax.set_xlabel(label)
axes units

關於「單位」支援的詳細資訊#

對日期和類別的支援是 Matplotlib 中內建的「單位」支援的一部分。這在 matplotlib.units基本單位 範例中有所說明。

單位支援的運作方式是查詢傳遞給繪圖函數的資料類型,並將其分派給列表中第一個接受該類型資料的轉換器。因此,如下所示,如果 x 中包含 datetime 物件,則轉換器將會是 _SwitchableDateConverter;如果其中包含字串,則會將其傳送到 StrCategoryConverter

for k, v in munits.registry.items():
    print(f"type: {k};\n    converter: {type(v)}")
type: <class 'decimal.Decimal'>;
    converter: <class 'matplotlib.units.DecimalConverter'>
type: <class 'numpy.datetime64'>;
    converter: <class 'matplotlib.dates._SwitchableDateConverter'>
type: <class 'datetime.date'>;
    converter: <class 'matplotlib.dates._SwitchableDateConverter'>
type: <class 'datetime.datetime'>;
    converter: <class 'matplotlib.dates._SwitchableDateConverter'>
type: <class 'str'>;
    converter: <class 'matplotlib.category.StrCategoryConverter'>
type: <class 'numpy.str_'>;
    converter: <class 'matplotlib.category.StrCategoryConverter'>
type: <class 'bytes'>;
    converter: <class 'matplotlib.category.StrCategoryConverter'>
type: <class 'numpy.bytes_'>;
    converter: <class 'matplotlib.category.StrCategoryConverter'>

有許多下游函式庫提供了它們自己的轉換器,並帶有定位器和格式器。物理單位支援由 astropypintunyt 等函式庫提供。

pandasnc-time-axis(以及 xarray)等高階函式庫提供了它們自己的日期時間支援。這種支援有時可能與 Matplotlib 原生的日期時間支援不相容,因此如果正在使用這些函式庫,在使用 Matplotlib 定位器和格式器時應謹慎。

腳本總執行時間: (0 分鐘 8.228 秒)

由 Sphinx-Gallery 產生圖庫