Pytanie Wspólne xlabel / ylabel dla subplots matplotlib


Mam następujący dział:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

a teraz chciałbym nadać temu wykresowi wspólne etykiety osi X i etykiety osi Y. Mówiąc "powszechny", mam na myśli, że pod całą siatką subplotów powinna znajdować się jedna duża etykieta osi x, a jedna duża etykieta osi y po prawej. Nie mogę znaleźć nic na ten temat w dokumentacji dla plt.subplots, a moje ruchy sugerują, że muszę zrobić duży plt.subplot(111) na początek - ale w jaki sposób mogę umieścić moje wątki 5 * 2 w tym użyciu plt.subplots?


76
2018-04-22 15:25


pochodzenie


Wraz z aktualizacją pytania i komentarzami pozostawionymi w poniższych odpowiedziach jest to duplikat stackoverflow.com/questions/6963035/... - Hooked
Niezupełnie, ponieważ moje pytanie dotyczy plt.subplots (), a pytanie, które łączysz z użyciem add_subplot - nie mogę użyć tej metody, chyba że zmienię na add_subplot, którego chciałbym uniknąć. ja mógłby użyj rozwiązania plt.text, które jest podane jako alternatywne rozwiązanie w twoim linku, ale nie jest to najbardziej eleganckie rozwiązanie. - jolindbe
Aby rozwinąć, o ile rozumiem, plt.subplots nie może wygenerować zestawu subplots w istniejącym środowisku osi, ale zawsze tworzy nową figurę. Dobrze? - jolindbe
Najbardziej eleganckie rozwiązanie można znaleźć tutaj: stackoverflow.com/questions/6963035/... - Mr.H
Twój link został dostarczony przez użytkownika Hooked ponad 4 lata temu (tylko kilka komentarzy powyżej twoich). Jak już wcześniej wspomniałem, to rozwiązanie dotyczy add_subplot, a nie plt.subplots (). - jolindbe


Odpowiedzi:


Wygląda na to, czego naprawdę chcesz. Stosuje to samo podejście ta odpowiedź w twoim konkretnym przypadku:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(nrows=3, ncols=3, sharex=True, sharey=True, figsize=(6, 6))

fig.text(0.5, 0.04, 'common X', ha='center')
fig.text(0.04, 0.5, 'common Y', va='center', rotation='vertical')

Multiple plots with common axes label


122
2017-11-12 16:56



genialny, po prostu genialny !! - ThePredator
zauważ, że 0,5 dla współrzędnej x etykiety x nie powoduje umieszczenia etykiety w środku środkowego subplot. musielibyście pójść nieco dalej, aby uwzględnić konto yticklabels. - dbliss
Spójrz na ta odpowiedź dla metody, która nie używa plt.text. Tworzysz swoje wątki, ale potem dodajesz jeden bitowy wykres, sprawisz, że będzie on niewidoczny i oznaczasz jego xiy. - kungfujam
Dzięki, pracowałem ogólnie. Jakiekolwiek rozwiązanie problemu złamania podczas używania tight_layout? - serv-inc
@ serv-inc z tight_layout zastępowanie 0.04 z 0 wydaje się działać. - divenex


Bez sharex=True, sharey=True dostajesz:

enter image description here

Dzięki niemu powinno być ładniej:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))

plt.tight_layout()

enter image description here

Ale jeśli chcesz dodać dodatkowe etykiety, powinieneś dodać je tylko do krawędzi:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))
        if i == len(axes2d) - 1:
            cell.set_xlabel("noise column: {0:d}".format(j + 1))
        if j == 0:
            cell.set_ylabel("noise row: {0:d}".format(i + 1))

plt.tight_layout()

enter image description here

Dodanie etykiety dla każdego wątku zepsułoby ją (być może istnieje sposób automatycznego wykrywania powtarzanych etykiet, ale nie jestem tego świadomy).


27
2018-05-13 18:21



Jest to o wiele trudniejsze, gdy na przykład liczba wykresów jest nieznana (np. Masz generalizowaną funkcję wydruku, która działa dla dowolnej liczby wątków). - naught101


Ponieważ polecenie:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

użyłeś zwraca krotkę składającą się z rysunku i listy instancji osi, wystarczy już coś zrobić (umysł, który zmieniłem fig,axdo fig,axes):

fig,axes = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

for ax in axes:
    ax.set_xlabel('Common x-label')
    ax.set_ylabel('Common y-label')

Jeśli chcesz zmienić niektóre szczegóły na konkretnym wątku, możesz uzyskać do niego dostęp za pośrednictwem axes[i] gdzie i iteruje po twoich wątkach.

Pomocne może być również dołączenie

fig.tight_layout()

na końcu pliku, przed plt.show(), aby uniknąć nakładania się etykiet.


14
2018-04-22 17:49



Przepraszam, że byłem nieco niejasny powyżej. Przy "wspólnym" miałem na myśli jedną etykietę x poniżej wszystkich działek i jedną etykietę y na lewo od działek, zaktualizowałem to pytanie, aby to odzwierciedlić. - jolindbe
@JohanLindberg: Odnośnie twoich komentarzy tutaj i powyżej: Rzeczywiście plt.subplots() utworzy nową instancję rysunku. Jeśli chcesz trzymać się tego polecenia, możesz łatwo dodać big_ax = fig.add_subplot(111), ponieważ masz już figurę i możesz dodać kolejną oś. Potem możesz manipulować big_ax sposób, w jaki jest pokazany w łączu od Hooked. - Marius
Dzięki za twoje sugestie, ale jeśli to zrobię, muszę dodać big_ax po plt.subplots (), i otrzymuję ten subplot na wszystko inne - czy mogę uczynić go przezroczystym lub jakoś go odesłać? Nawet jeśli ustawię wszystkie kolory na zerowe, tak jak w łączu Hooked's, nadal jest to białe pole obejmujące wszystkie moje wątki. - jolindbe
@JohanLindberg, masz rację, nie sprawdziłem tego. Ale możesz łatwo ustawić kolor tła wielkiej osi none wykonując: big_ax.set_axis_bgcolor('none') Powinieneś również zrobić kolor etykiety none (w przeciwieństwie do przykładu związanego z Hooked): big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off') - Marius
Pojawia się błąd: AttributeError: 'numpy.ndarray' object has no attribute 'set_xlabel' w oświadczeniu ax.set_xlabel('Common x-label'). Czy możesz to rozgryźć? - hengxin


Podczas rysowania siatki wykresów napotkałem podobny problem. Wykresy składały się z dwóch części (góra i dół). Etykieta y miała być wycentrowana na obie części.

Nie chciałem używać rozwiązania, które zależy od znajomości położenia na zewnętrznej figurze (jak fig.text ()), więc zmanipulowałem pozycję y funkcji set_ylabel (). Zazwyczaj jest to 0,5, środek wykresu, do którego został dodany. Ponieważ odstęp pomiędzy częściami (hspace) w moim kodzie był zerowy, mógłbym obliczyć środek dwóch części względem górnej części.

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

# Create outer and inner grid
outerGrid = gridspec.GridSpec(2, 3, width_ratios=[1,1,1], height_ratios=[1,1])
somePlot = gridspec.GridSpecFromSubplotSpec(2, 1,
               subplot_spec=outerGrid[3], height_ratios=[1,3], hspace = 0)

# Add two partial plots
partA = plt.subplot(somePlot[0])
partB = plt.subplot(somePlot[1])

# No x-ticks for the upper plot
plt.setp(partA.get_xticklabels(), visible=False)

# The center is (height(top)-height(bottom))/(2*height(top))
# Simplified to 0.5 - height(bottom)/(2*height(top))
mid = 0.5-somePlot.get_height_ratios()[1]/(2.*somePlot.get_height_ratios()[0])
# Place the y-label
partA.set_ylabel('shared label', y = mid)

plt.show()

obrazek

Wady:

  • Pozioma odległość do wykresu jest oparta na górnej części, dolne tiki mogą rozciągać się na etykiecie.

  • Formuła nie uwzględnia spacji między częściami.

  • Zgłasza wyjątek, gdy wysokość górnej części wynosi 0.

Prawdopodobnie istnieje ogólne rozwiązanie uwzględniające dopełnienie między cyframi.


2
2017-07-27 16:35



Hej, wymyśliłem sposób, aby zrobić to bardzo dokładnie w odpowiedzi na twoje pytanie, ale może rozwiązać niektóre z tych problemów; widzieć stackoverflow.com/a/44020303/4970632 (poniżej) - Luke Davis


Odkryłem bardziej niezawodną metodę:

Jeśli znasz bottom i top kwargs, który wszedł w GridSpec inicjalizacja, lub inaczej znasz położenie krawędzi twoich osi Figure współrzędne, możesz także określić pozycję ylabel w Figure współdziała z pewną fantazyjną "transformującą" magią. Na przykład:

import matplotlib.transforms as mtransforms
bottom, top = .1, .9
f, a = plt.subplots(nrows=2, ncols=1, bottom=bottom, top=top)
avepos = (bottom+top)/2
a[0].yaxis.label.set_transform(mtransforms.blended_transform_factory(
       mtransforms.IdentityTransform(), f.transFigure # specify x, y transform
       )) # changed from default blend (IdentityTransform(), a[0].transAxes)
a[0].yaxis.label.set_position((0, avepos))
a[0].set_ylabel('Hello, world!')

... i powinieneś zobaczyć tę etykietę nadal odpowiednio dostosowuje lewy-prawy aby nie nakładać się na siebie z ticklabels, tak jak zwykle - ale teraz dostosuje się, aby być zawsze dokładnie pomiędzy pożądane partycje.

Ponadto, jeśli nawet nie używasz set_position, ylabel pojawi się domyślnie dokładnie w połowie wysokości. Zgaduję, że to dlatego, że kiedy etykieta jest ostatecznie narysowana, matplotlib używa 0.5 dla y-koordynacja bez sprawdzania, czy transformacja współrzędnych uległa zmianie.


1
2018-05-17 09:07





Będzie wyglądać lepiej, jeśli zarezerwujesz miejsce dla wspólnych etykiet, tworząc niewidoczne etykiety dla subplot w lewym dolnym rogu. Dobrze jest także przekazać fontsize z rcParams. W ten sposób wspólne etykiety zmienią rozmiar wraz z konfiguracją rc, a osie zostaną również dostosowane, aby pozostawić miejsce na wspólne etykiety.

fig_size = [8, 6]
fig, ax = plt.subplots(5, 2, sharex=True, sharey=True, figsize=fig_size)
# Reserve space for axis labels
ax[-1, 0].set_xlabel('.', color=(0, 0, 0, 0))
ax[-1, 0].set_ylabel('.', color=(0, 0, 0, 0))
# Make common axis labels
fig.text(0.5, 0.04, 'common X', va='center', ha='center', fontsize=rcParams['axes.labelsize'])
fig.text(0.04, 0.5, 'common Y', va='center', ha='center', rotation='vertical', fontsize=rcParams['axes.labelsize'])

enter image description here enter image description here


0
2018-05-30 17:57