Issue
I have a fairly lengthy program that I've been working on to do some data analysis at my lab. It takes a csv file and calculates a limit of detection for one or more gene targets based on a range of concentrations of input DNA (RNA, actually, but it's irrelevant in this case).
Since the number of targets to be evaluated is variable, I wrote two functions - one makeplots(targets)
that returns a figure with subplots (arranged how I want them, depending on how many there are) and the array of axes for the subplots. After some data processing and calculations, my drawplots(ax[i], x, y, [other variables for less-relevant settings])
function is called within a loop that's iterating over the array of data tables for each target.
makeplots()
works fine - everything's where I want it, shaped nicely, etc etc. But as soon as drawplots()
gets called, the scales get warped and the plots look awful.
The code below is not the original script (though the functions are the same) -- I cut out most of the processing and input steps and just defined the variables and arrays as they end up when working with some test data. This is only for two targets; I haven't tried with 3+ yet as I want to get the simpler case in order first.
(Forgive the lengthy import block; I just copied it from the real script. I'm a bit pressed for time and didn't want to fiddle with the imports in case I deleted one that I actually still needed in this compressed example)
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)
I want the subplots to stay square, so if the x-values for one subplot fall in the range [0,50] and another another are all within [0,2], the x-axes should rescale to the same length. Attached are a 'good' (but blank) figure and a 'bad' figure (as currently gets spit out) good plots! bad plots!
Solution
Inside drawplots()
about line 82 in your code, try using ax.set_aspect('auto')
(you currently have it set to ax.set_aspect('equal')
) Setting it to 'auto'
produced the graphs you are looking for:
Answered By - rob_7cc
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.