Mam następującą fabułę:
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 Y. Przez „wspólne” mam na myśli, że pod całą siatką wykresów cząstkowych powinna znajdować się jedna duża etykieta osi X, a po prawej stronie jedna duża etykieta osi y. Nie mogę znaleźć nic na ten temat w dokumentacji dla plt.subplots
, a moi pracownicy Google sugerują, że muszę plt.subplot(111)
zacząć od czegoś dużego - ale jak następnie umieścić w tym wątki 5 * 2 plt.subplots
?
python
matplotlib
jolindbe
źródło
źródło
Odpowiedzi:
To wygląda na to, czego naprawdę chcesz. Stosuje to samo podejście tej odpowiedzi do konkretnego 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')
źródło
plt.text
. Tworzysz podploty, a następnie dodajesz jeden bitowy wykres, sprawiasz, że jest niewidoczny i oznaczasz jego x i y.tight_layout
?tight_layout
wymianą0.04
ze0
wydaje się działać.fig.text
nie jest dobrym pomysłem. To psuje takie rzeczy, jakplt.tight_layout()
Ponieważ uważam to za odpowiednie i wystarczająco eleganckie (nie ma potrzeby podawania współrzędnych, aby umieścić tekst), kopiuję (z niewielkim dostosowaniem) odpowiedź na inne powiązane pytanie .
import matplotlib.pyplot as plt fig, axes = plt.subplots(5, 2, sharex=True, sharey=True, figsize=(6,15)) # add a big axis, hide frame fig.add_subplot(111, frameon=False) # hide tick and tick label of the big axis plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False) plt.xlabel("common X") plt.ylabel("common Y")
Z tego wynika (w wersji matplotlib 2.2.0):
źródło
constrained_layout=True
ponieważ tworzy nakładające się etykiety. W takim przypadku musisz ręcznie dostosować granice działek pomocniczych.Bez
sharex=True, sharey=True
Ciebie otrzymasz:Dzięki niemu powinieneś ł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()
Ale jeśli chcesz dodać dodatkowe etykiety, powinieneś dodać je tylko do wydruków 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()
Dodanie etykiety do każdego wykresu zepsułoby to (być może istnieje sposób na automatyczne wykrycie powtarzających się etykiet, ale ja nie znam żadnego).
źródło
Od polecenia:
fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)
użyłeś zwraca krotkę składającą się z figury i listy instancji osi, wystarczy już zrobić coś takiego (pamiętaj, że zmieniłem
fig,ax
nafig,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 zdarzy ci się chce zmienić kilka szczegółów na konkretnym poletko, można uzyskać do niego dostęp za pośrednictwem
axes[i]
gdziei
iteracje nad swoimi wątków.Bardzo pomocne może być również dołączenie pliku
na końcu pliku, przed
plt.show()
, aby uniknąć nakładania się etykiet.źródło
plt.subplots()
utworzy nową instancję figury. Jeśli chcesz trzymać się tego polecenia, możesz łatwo dodać abig_ax = fig.add_subplot(111)
, ponieważ masz już figurę i możesz dodać kolejną oś. Następnie możesz manipulowaćbig_ax
sposobem, w jaki jest wyświetlany w linku z Hooked.none
, wykonując następujące czynności:big_ax.set_axis_bgcolor('none')
Powinieneś także zrobić labelcolornone
(w przeciwieństwie do przykładu połączonego przez Hooked):big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
AttributeError: 'numpy.ndarray' object has no attribute 'set_xlabel'
w wyciąguax.set_xlabel('Common x-label')
. Czy możesz to rozgryźć?Będzie wyglądać lepiej, jeśli zarezerwujesz miejsce na wspólne etykiety, tworząc niewidoczne etykiety dla wykresu pomocniczego w lewym dolnym rogu. Dobrze jest również przekazać rozmiar czcionki z rcParams. W ten sposób wspólne etykiety zmienią rozmiar wraz z konfiguracją rc, a osie zostaną również dostosowane, aby zostawić 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'])
źródło
Podobny problem napotkałem podczas kreślenia siatki wykresów. Wykresy składały się z dwóch części (górnej i dolnej). Etykieta Y miała być wyśrodkowana na obu częściach.
Nie chciałem używać rozwiązania polegającego na znajomości pozycji na zewnętrznej figurze (np. Rys.text ()), więc manipulowałem pozycją y funkcji set_ylabel (). Zwykle wynosi 0,5, środek działki, do którego jest dodawany. Ponieważ wypełnienie między częściami (hspace) w moim kodzie wynosiło zero, mogłem obliczyć środek dwóch części w stosunku do 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 znaczniki mogą sięgać do etykiety.
Formuła nie uwzględnia odstępów 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 wypełnienie między liczbami.
źródło
Aktualizacja:
Ta funkcja jest teraz częścią pakietu proplot matplotlib, który niedawno wydałem na pypi. Domyślnie podczas tworzenia figur etykiety są „wspólne” między osiami.
Oryginalna odpowiedź:
Odkryłem solidniejszą metodę:
Jeśli znasz
bottom
itop
kwargs, które przeszły podczasGridSpec
inicjalizacji, lub w inny sposób znasz położenie krawędzi swoich osi weFigure
współrzędnych , możesz również określić położenie ylabel weFigure
współrzędnych za pomocą jakiejś fantazyjnej magii „transformacji”. 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ć, że etykieta nadal odpowiednio dostosowuje się od lewej do prawej, aby nie nakładać się na etykiety znaczników, tak jak zwykle - ale teraz dostosuje się, aby zawsze znajdować się dokładnie między pożądanymi polami.
Ponadto, jeśli nawet nie używasz
set_position
, ylabel pojawi się domyślnie dokładnie w połowie liczby . Zgaduję, że dzieje się tak dlatego, że kiedy etykieta jest ostatecznie rysowana,matplotlib
używa 0,5 dlay
-korzędnej bez sprawdzania, czy podstawowa transformacja współrzędnych uległa zmianie.źródło