Оси подзаголовка Matplotlib изменяют размер после построения данных

У меня есть довольно длинная программа, над которой я работаю, чтобы провести анализ данных в моей лаборатории. Он берет файл csv и вычисляет предел обнаружения для одного или нескольких генов-мишеней на основе диапазона концентраций входной ДНК (фактически РНК, но в данном случае это не имеет значения).

Поскольку число оцениваемых целей является переменным, я написал две функции — одну makeplots(targets), которая возвращает фигуру с подграфиками (расположенными так, как я хочу, в зависимости от их количества), и массив осей для подграфиков. После некоторой обработки данных и вычислений моя функция drawplots(ax[i], x, y, [other variables for less-relevant settings]) вызывается в цикле, который перебирает массив таблиц данных для каждой цели.

makeplots() работает нормально - все там, где я хочу, красиво оформлено и т. д. и т. д. Но как только вызывается drawplots(), масштабы искажаются, а графики выглядят ужасно.

Приведенный ниже код не является исходным скриптом (хотя функции те же) — я вырезал большую часть шагов обработки и ввода и просто определил переменные и массивы в том виде, в каком они заканчиваются при работе с некоторыми тестовыми данными. Это только для двух целей; Я еще не пробовал с 3+, так как хочу сначала привести в порядок более простой случай.

(Простите за длинный блок импорта, я просто скопировал его из настоящего скрипта. У меня немного времени и я не хотел возиться с импортом на случай, если я удалю тот, который мне действительно все еще нужен в этом сжатом примере)

import os
import sys
import time
import math
import scipy
import datetime
import warnings
import collections
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import win32gui as wg
from win32gui import GetWindowText, GetForegroundWindow
from scipy.stats import norm
from tkinter import filedialog
from tkinter import *


actwindow = (GetForegroundWindow())
root = Tk()
root.withdraw()
cbcolors = ['#377eb8', '#f781bf', '#dede00', '#999999',
            '#4daf4a', '#984ea3', '#e41a1c', '#ff7f00',
            '#000000', '#a65628']
params = {'mathtext.default': 'regular'}
plt.rcParams.update(params)

#######################################################
#           create figure and array of axes           #
#######################################################
def makeplots(targets, active=actwindow):
    numsubs = len(targets)
    def rowcnt(y):
        rownumb = (y//3) if y%3 == 0 else (1+(y//3))
        return rownumb
    def colcnt(x):
        if x <= 3: colnumb = x
        elif x == 4: colnumb = 2
        else: colnumb = 3
        return colnumb
    if numsubs >= 1:
        fig, axs = plt.subplots(num='LLoD', nrows=rowcnt(numsubs), ncols=colcnt(numsubs), sharey='row', figsize = [colcnt(numsubs)*5,rowcnt(numsubs)*6], subplot_kw={'adjustable': 'box', 'aspect': 1})
        fig.text(0.04, 0.5, 'Probit score\n    $(\sigma + 5)$', va='center', rotation='vertical', size='18')
    else:
        raise ValueError('Error generating plots -- number of targets must be a positive integer.')
    axs = np.ravel(axs)
    for i, ax in enumerate(axs):
        ax.set_title(f'Limit of Detection: {targets[i]}', size=11)
        ax.grid()
    return fig, axs

#######################################################
#                 draw the actual plots               #
#######################################################
def drawplots(ax, x, y, logans, color1, color2):
    y95 = 6.6448536269514722
    regfun = lambda m, x, b : (m*x) + b
    regression = scipy.stats.linregress(x,y)
    slope, intercept, r = regression.slope, regression.intercept, regression.rvalue
    r2 = r**2

    while True:
        if logans == 'y':
            x_label = '$log_{10}$(input quantity)'
            break
        elif logans == 'n':
            x_label = 'input quantity'
            break
        raise ValueError('Error calling drawplots() - invalid input')

    lod = (y95-intercept)/slope
    xr = [0, lod*1.2]
    yr = [intercept, regfun(slope, xr[1], intercept)]
    regeqn = "y = " + str(f"{slope:.3f}") +"x + " + str(f"{intercept:.3f}")

    ax.set_xlabel(x_label)
    ax.plot(xr, yr, color=color1, linestyle='--') # plot linear regression of data
    ax.plot(lod, y95, marker='o', color=color2, markersize=7) # marks the limit of detection
    ax.plot(xr, [y95,y95], color=color2, linestyle=':') # horiz. line to lod
    ax.plot([lod,lod], [0, 7.1], color=color2, linestyle=':') # vert. line to lod
    ax.plot(x, y, color=color1, marker='x') # plot lod data points
    ax.set_aspect('equal')
    ax.set_xlim(left=0)
    ax.plot()

    return r2, lod, regeqn
#######################################################
#                      main script                    #
#######################################################
numTar = 2
logans, logconv = 'y', 'n'
targets = ['target1','target2']
qtys = np.array([6, 5, 4.7, 4, 3.7, 3, 2.7, 2.4, 2, 1.7])
prop = np.array([1, 1, 0.8, 0.2, 0.1, 0, 0, 0.1, 0.1, 0, 1,
                1, 1, 1, 1, 1, 0.9, 0.8, 0.3, 0.3]).reshape(2,10)
probit = np.array([np.inf, np.inf, 5.84162123, 4.15837877, 3.71844843,
                -np.inf, -np.inf, 3.71844843, 3.71844843, -np.inf,
                np.inf, np.inf, np.inf, np.inf, np.inf, np.inf,
                6.28155157, 5.84162123, 4.47559949, 4.47559949]).reshape(2,10)
log_qtys = np.zeros([len(qtys)])
tables = np.zeros([numTar, len(qtys), 4])
ht_collection = collections.OrderedDict()
for x, qty in enumerate(qtys): 
    log_qtys[x] = math.log10(qty)

for idx, tar in enumerate(targets):
    tables[idx,:,0] = qtys
    tables[idx,:,1] = log_qtys
    for i, val in enumerate(qtys):
        tables[idx,i,2] = prop[idx,i]
        tables[idx,i,3] = probit[idx,i]

# this is where makeplots is called. figure, subplots, and axes
# look beautiful at this point
fig, axs = makeplots(targets)

for i, tars in enumerate(targets):
    ht_collection[tars] = pd.DataFrame(tables[i,:,:], columns=["qty","log_qty","probability","probit"])
    ht_collection[tars].probit.replace([np.inf, -np.inf], np.nan, inplace=True)
    ht_collection[tars].dropna(inplace=True)

# this is where drawplots() is called and the figure/plots get uglified
    while True:
        if logans == 'y':
            r2, lod, eqn = drawplots(axs[i], ht_collection[tars].log_qty, ht_collection[tars].probit, logans, cbcolors[i], cbcolors[i+5])
            break
        elif logans == 'n':
            r2, lod, eqn = drawplots(axs[i], ht_collection[tars].qty, ht_collection[tars].probit, logans, cbcolors[i], cbcolors[i+5])
        raise ValueError(f"Error calling drawplots() - subplot {i+1} of {len(targets)+1}")
    if logconv == 'y': lod **=10

plt.ion()
#plt.savefig(f"{dt} LLoD Analysis at {tm}.png", format='png')
plt.show()
plt.pause(0.001)

Я хочу, чтобы подграфики оставались квадратными, поэтому, если значения x для одного подграфика попадают в диапазон [0,50], а другой — в пределах [0,2], оси x должны масштабироваться до одинаковой длины. Прикреплены «хорошая» (но пустая) цифра и «плохая» цифра (как в настоящее время выплевывается) хорошие сюжеты! плохие сюжеты!


person W. MacTurk    schedule 30.03.2021    source источник


Ответы (1)


Внутри drawplots() около строки 82 в вашем коде попробуйте использовать ax.set_aspect('auto') (в настоящее время у вас установлено значение ax.set_aspect('equal')). Установка его на 'auto' привела к получению графиков, которые вы ищете: введите здесь описание изображения

person rob_7cc    schedule 30.03.2021
comment
Хм, интересно. Думаю, я неправильно истолковал соотношение сторон сюжета в целом с соотношением сторон единиц на осях, что, по-видимому, и означает matplotlib. Благодарю вас! Пока я занимаюсь этим, как я могу убрать линии, соединяющие точки данных? Мне нужны только точки, пунктирная линия регрессии и пунктирные линии «перекрестия». - person W. MacTurk; 30.03.2021
comment
Добавьте linestyle='None' к вызову сюжета примерно в строке 81: ax.plot(x, y, color=color1, marker='x', linestyle='None') # plot lod data points - person rob_7cc; 31.03.2021
comment
Обновление: ax.scatter() вызывал у меня проблемы в прошлом, но теперь, похоже, работает нормально, поэтому мой вопрос о линиях, соединяющих точки данных, решен. - person W. MacTurk; 31.03.2021
comment
К сожалению, страница не обновлялась до того, как я добавил свой последний комментарий. linestyle='None' также устранил проблему, но до тех пор, пока/если ax.scatter() снова не вызовет у меня проблем, я просто буду использовать это для краткости (и чтобы сделать код более читабельным - легче увидеть, какие вызовы предназначены для построения линий, а какие для построения графика). точек с помощью двух разных ax методов - person W. MacTurk; 31.03.2021