Измените количество десятичных знаков на триплоте с помощью matplotlib только на одном тике подзаголовка - PullRequest
2 голосов
/ 14 июля 2020

Я рисую то, что мы называем триплотом, то есть совместным распределением с парой параметров, которые представляют ковариацию и апостериорное распределение. Он состоит из разных подзаголовков.

Первоначально на подзаголовке на оси Y триплота я получаю следующие значения xticks по умолчанию:

enter image description here

What I would like to do is to replace mean xtick value 0.0000 by 0.00.

For this and from my research, I found (with keeping Latex fonts) :

## If we are on third line of triplot
if (m == 3):
  ax.yaxis.set_major_formatter(FormatStrFormatter(r'$%.2f$')) (1)

The issue is that I have now the following subplot :

enter image description here

As you can see, the two other xtick values are also affected by the format %.2f whereas I just want to use this format for the mean (0.00).

How can I keep the limits ±0.1864 and change only the number of decimals for 0.0000 to display only 0.00 ?

I think I have to select the right xtick (what I call the "mean[m]" in my code indexed by "m" where "m" represents the line of triplot subplots (and here with a column index equal to 0 = first column) along all the y-axis but I don't know how to locate it on this axis.

If someone could see how to change only this value by adding/modifying some options to the (1) instruction line, I would be grateful.

UPDATE 1: Thanks for your answers. Actually, I am using the tool GetDist (с примерами на GetDist examples ).

Первоначально я подумал, что, взяв 2 десятичных знака вместо 4 для параметра «w_a» (метка слева на рисунках выше) , метка "w_a" будет смещена вправо.

Действительно, я показываю вам ниже весь график tri angular, где вы можете видеть , что эта метка «w_a» смещена по сравнению ко всем остальным меткам , как по оси Ox, так и по оси Oy: по оси Ox «w_a» не выровнен, а для Oy он выровнен по левому краю, поскольку «-0,1864» и «0,1864» (в основном я думаю, что с знак минус перед 0,1864).

Как вы думаете, может быть уловка / решение, чтобы выровнять все метки с постоянным промежутком между ними и осями Ox и Oy? Я имею в виду независимо от того факта, что я беру 2 или 4 десятичных знака для 3 xticks на каждом поле.

triangular plot

UPDATE 2: I have found a solution that we can qualify of "tricky" or "dirty" solution.

I played with set_label_coords on xaxis and yaxis and set manually the padding which gives a good rendering.

g = plots.get_subplot_plotter()
g.settings.constrained_layout = True

# Call triplot
g.triangle_plot([matrix1, matrix2],
                names,
                filled = True,
                legend_labels = ['1240', '1560'],
                legend_loc = 'upper right',
                contour_colors = ['red','darkblue'],
                line_args = [{'lw':2, 'color':'red'},
                {'lw':2, 'color':'darkblue'}],
                )

# Browse each subplot of triangle
for m in range(0,7):
  for n in range(0,m+1):
    ax = g.subplots[m,n]

    # First joint disctibution : row = 1 (second line) and column = 0 (first column)
    if (m == 1 and n == 0):
      x0, y0 = ax.yaxis.get_label().get_position()

    if (m != n):
      if (m == 3 and n == 0):
        # shift on the left w_a label
        ax.yaxis.set_label_coords(x0-0.55, y0)

        # Joint disctibution : row = 3 (fourth line) and column = 0 (first column)
        # 0.0 for w_a
        ax.yaxis.set_major_formatter(FuncFormatter(lambda x,_: '$0.0$' if x == 0 else f'${x:.4f}$'))
       
      # Joint disctibution : row = 6 (seventh line) and column = 3 (fourth column)

      if (m == 6 and n == 3):
        # shift on bottom w_a label
        ax.xaxis.set_label_coords(x0+0.5, y0-1)
      elif (n == 3):
        ax.xaxis.set_major_formatter(FuncFormatter(lambda x,_: '$0.0$' if x == 0 else f'${x:.4f}$'))

Here is the result :

Tricky solution

As you can see, this is a fine-tuned solution since I have to iteratively change the padding to get in agreement with all the other labels from a global alignment point of view.

My original idea was to store the coordinates of the first label on each axis and apply these coordinates to all other labels on each side subplots.

But unfortunately, I get for each side subplots for y-axis :

x0 = 0, y0 = 0.5

and for x-axis : x0 = 0.5 and y0 = 0

So, If I apply these coordinates, the labels are over on x-axis/y-axis and I am obliged to shift horizontally (for y-axis) and vertically (for x-axis) the labels.

But the ideal would be to have an automatic way to align all the labels at the same space between labels and axis. I keep on my research.

UPDATE 3: I tried to apply the solution given by @JohanCwith the following code snippet at the end of my script :

for ax in g.fig.axes:
    ylabel = ax.get_ylabel()
    if len(ylabel) > 0:
        ylbl = ax.yaxis.get_label()
        ax.yaxis.labelpad = ylbl.get_position()[0] + 30

Here the results :

@JohanCsuggestion

As you can see, an extra padding is appearing for "w_a" parameter compared to all the others labels.

I think that extra-padding is simply caused by the value -0.1864 on the y-axis of subplot corresponding to "w_a" label, especially by the sign "minus" which pushes the label to the left, always compared to the others labels which have 4 decimals without sign "minus".

I don't know how to force the label "w_a" to be located like the other ones (this is your code snippet).

Is there an alternative way to do this forcing ?

PS : maybe if I can't force the same location for "w_a" parameter**, I could include in the @JohanC solution, i.e in the for ax in g.fig.axes: a condition dedicated for "w_a" by making decrease the labelpad and then set it roughly to the same labelpad than the other labels.

UPDATE 4 : Following the advices of JohanC, I dit at the end of my script :

for ax in g.fig.axes:
    ylabel = ax.get_ylabel()
    if len(ylabel) > 0:
        ylbl = ax.yaxis.get_label()
        ax.yaxis.labelpad = ylbl.get_position()[0] + 30

# Redraw the plot
plt.draw()
# Save triplot
g.export('Final_Figure.pdf')

But the Final_figure.pdf is not changing the shift of "w_a" (left shift for Oy axis and bottom shift for Ox axis), below the result :

Всегда переключайтесь с

Я не знаю, что еще попробовать.

Ответы [ 2 ]

4 голосов
/ 14 июля 2020

Вы можете использовать специальный форматировщик тиков . Jakevdp также имеет хороший пример , включающий выражения с π в качестве галочки.

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

def custom_tick_formatter(x, pos):
    'The two args are the value and tick position'
    return f'${x:.2f}$' if abs(x) < 1e-5 else f'${x:.4f}$'

fig, ax = plt.subplots()
t = np.arange(1, 51)
ax.scatter(t * np.cos(t) / 200, t * np.sin(t) / 200)
ax.yaxis.set_major_formatter(FuncFormatter(custom_tick_formatter))
ax.set_yticks([-0.1864, 0, 0.1864])
plt.show()

example plot

About your new question, it seems just setting the position of the ylabel gets overridden when the plot is redrawn. Setting the padding of the ylabel depending on its position seems to work well. Try to add this at the end of your code (maybe the value 30 needs to change in your situation):

for ax in g.fig.axes:
    ylabel = ax.get_ylabel()
    if len(ylabel) > 0:
        ylbl = ax.yaxis.get_label()
        # print(ylabel, ylbl.get_position())
        # ax.set_ylabel(ylabel, labelpad=...)
        ax.yaxis.labelpad = ylbl.get_position()[0] - 30
plt.show()
4 голосов
/ 14 июля 2020

Вы можете использовать FuncFormatter:

from matplotlib.ticker import FuncFormatter

ax.yaxis.set_major_formatter(FuncFormatter(lambda x,_: '0.00' if x == 0 else f'{x:.4f}'))

Что касается вашего обновленного (фактического) вопроса: вы можете использовать align_labels() .

Пример:

from getdist import plots, MCSamples
import getdist
import matplotlib.pyplot as plt
import numpy as np
ndim = 4
nsamp = 1000
np.random.seed(10)
A = np.random.rand(ndim,ndim)
cov = np.dot(A, A.T)
samps = np.random.multivariate_normal([0]*ndim, cov, size=nsamp)
samps[:,1]-=100000
A = np.random.rand(ndim,ndim)
cov = np.dot(A, A.T)
samps2 = np.random.multivariate_normal([0]*ndim, cov, size=nsamp)
names = ["x%s"%i for i in range(ndim)]
labels =  ["x_%s"%i for i in range(ndim)]
samples = MCSamples(samples=samps,names = names, labels = labels)
samples2 = MCSamples(samples=samps2,names = names, labels = labels, label='Second set')
g = plots.get_subplot_plotter()
g.settings.axis_tick_x_rotation=45
g.triangle_plot([samples, samples2], filled=True)

enter image description here

Then after g.fig.align_labels(): введите описание изображения здесь

(сохраняется в PDF-файле, сохраненном с g.export('Final_Figure.pdf') или g.fig.savefig('Final_Figure.pdf')).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...