2. Getting started with prtecan#

[1]:
%load_ext autoreload
%autoreload 2

import os
import warnings
from pathlib import Path

import arviz as az
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sb

from clophfit import prtecan
from clophfit.binding import fitting, plotting
from clophfit.prtecan import Titration

data_tests = (Path("..") / ".." / "tests" / "Tecan").resolve().absolute()

plt.show()
2025-06-23 11:54:45,260 - clophfit.binding.fitting - INFO - Plotting module started
[2]:
os.chdir(data_tests / "L1")
warnings.filterwarnings("ignore", category=UserWarning, module="clophfit.prtecan")

2.1. Parsing a Single Tecan Files#

A Tecan file comprises of multiple label blocks, each with its unique metadata. This metadata provides critical details and context for the associated label block. In addition, the Tecan file itself also has its overarching metadata that describes its overall content.

When the KEYS for label blocks are identical, it indicates that these label blocks are equivalent - meaning, they contain the same measurements. The equality of KEYS plays a significant role in parsing and analyzing Tecan files, as it assists in identifying and grouping similar measurement sets together. This understanding of label block equivalence based on KEY similarity is critical when working with Tecan files.

[3]:
tf = prtecan.Tecanfile("290513_8.8.xls")
lb1 = tf.labelblocks[1]
lb2 = tf.labelblocks[2]
tf.metadata
[3]:
{'Device: infinite 200': Metadata(value='Serial number: 810002712', unit=['Serial number of connected stacker:']),
 'Firmware: V_2.11_04/08_InfiniTe (Apr  4 2008/14.37.11)': Metadata(value='MAI, V_2.11_04/08_InfiniTe (Apr  4 2008/14.37.11)', unit=None),
 'Date:': Metadata(value='29/05/2013', unit=None),
 'Time:': Metadata(value='11.59.26', unit=None),
 'System': Metadata(value='TECANROBOT', unit=None),
 'User': Metadata(value='TECANROBOT\\Administrator', unit=None),
 'Plate': Metadata(value='PE 96 Flat Bottom White   [PE.pdfx]', unit=None),
 'Plate-ID (Stacker)': Metadata(value='Plate-ID (Stacker)', unit=None)}
[4]:
print("Metadata:\n", lb1.metadata, "\n")
print("Data:\n", lb1.data)
Metadata:
 {'Label': Metadata(value='Label1', unit=None), 'Mode': Metadata(value='Fluorescence Top Reading', unit=None), 'Excitation Wavelength': Metadata(value=400, unit=['nm']), 'Emission Wavelength': Metadata(value=535, unit=['nm']), 'Excitation Bandwidth': Metadata(value=20, unit=['nm']), 'Emission Bandwidth': Metadata(value=25, unit=['nm']), 'Gain': Metadata(value=94, unit=['Optimal']), 'Number of Flashes': Metadata(value=10, unit=None), 'Integration Time': Metadata(value=20, unit=['µs']), 'Lag Time': Metadata(value='µs', unit=None), 'Settle Time': Metadata(value='ms', unit=None), 'Start Time:': Metadata(value='29/05/2013 11.59.52', unit=None), 'Temperature': Metadata(value=25.4, unit=['°C']), 'End Time:': Metadata(value='29/05/2013 12.00.30', unit=None)}

Data:
 {'A01': 17123.0, 'A02': 19477.0, 'A03': 20346.0, 'A04': 20322.0, 'A05': 23189.0, 'A06': 21656.0, 'A07': 19716.0, 'A08': 22933.0, 'A09': 24845.0, 'A10': 26932.0, 'A11': 24703.0, 'A12': 31320.0, 'B01': 19915.0, 'B02': 20707.0, 'B03': 34870.0, 'B04': 19916.0, 'B05': 21555.0, 'B06': 20760.0, 'B07': 26798.0, 'B08': 21530.0, 'B09': 24059.0, 'B10': 22748.0, 'B11': 23850.0, 'B12': 29973.0, 'C01': 13223.0, 'C02': 14025.0, 'C03': 16139.0, 'C04': 18865.0, 'C05': 21233.0, 'C06': 20513.0, 'C07': 32580.0, 'C08': 23449.0, 'C09': 22229.0, 'C10': 27775.0, 'C11': 26371.0, 'C12': 28211.0, 'D01': 17795.0, 'D02': 17551.0, 'D03': 19378.0, 'D04': 20682.0, 'D05': 21870.0, 'D06': 23351.0, 'D07': 21868.0, 'D08': 22034.0, 'D09': 22020.0, 'D10': 25802.0, 'D11': 25501.0, 'D12': 27969.0, 'E01': 20430.0, 'E02': 20843.0, 'E03': 19416.0, 'E04': 20888.0, 'E05': 22007.0, 'E06': 23131.0, 'E07': 22137.0, 'E08': 22253.0, 'E09': 23164.0, 'E10': 32320.0, 'E11': 22974.0, 'E12': 22002.0, 'F01': 19330.0, 'F02': 19580.0, 'F03': 22396.0, 'F04': 27164.0, 'F05': 22089.0, 'F06': 24924.0, 'F07': 22568.0, 'F08': 39721.0, 'F09': 22186.0, 'F10': 32362.0, 'F11': 35322.0, 'F12': 28114.0, 'G01': 23505.0, 'G02': 20463.0, 'G03': 33826.0, 'G04': 22191.0, 'G05': 21804.0, 'G06': 22721.0, 'G07': 23578.0, 'G08': 21727.0, 'G09': 34636.0, 'G10': 26413.0, 'G11': 35600.0, 'G12': 34854.0, 'H01': 19143.0, 'H02': 33593.0, 'H03': 27593.0, 'H04': 39346.0, 'H05': 41586.0, 'H06': 23899.0, 'H07': 22724.0, 'H08': 24544.0, 'H09': 23636.0, 'H10': 24116.0, 'H11': 29481.0, 'H12': 28309.0}
[5]:
tf1 = prtecan.Tecanfile("290513_8.2.xls")

tf1.labelblocks[1].__almost_eq__(lb1), tf1.labelblocks[1] == lb1
[5]:
(True, True)

2.2. Titration inherits TecanfilesGroup#

[6]:
tfg = prtecan.TecanfilesGroup([tf, tf1])
lbg1 = tfg.labelblocksgroups[1]

print(lbg1.data["A01"])

lbg1.data_nrm["A01"]
WARNING - Different LabelblocksGroup across files: ['290513_8.8.xls', '290513_8.2.xls'].
[17123.0, 17255.0]
[6]:
[910.7978723404256, 917.8191489361702]
[7]:
tit = prtecan.Titration([tf, tf1], x=np.array([8.8, 8.2]), is_ph=True)
print(tit)
tit.labelblocksgroups[1].data_nrm["A01"]
WARNING - Different LabelblocksGroup across files: ['290513_8.8.xls', '290513_8.2.xls'].
Titration
        files=["290513_8.8.xls", ...],
        x=[np.float64(8.8), np.float64(8.2)],
        x_err=[],
        labels=dict_keys([1, 2]),
        params=TitrationConfig(bg=True, bg_adj=False, dil=True, nrm=True, bg_mth='mean', mcmc=False)    pH=True additions=[]
        scheme=PlateScheme(file=None, _buffer=[], _discard=[], _ctrl=[], _names={}))
[7]:
[910.7978723404256, 917.8191489361702]
[8]:
tit.labelblocksgroups == tfg.labelblocksgroups
[8]:
True
[9]:
tit.additions = [100, 1]

tit.params.nrm = True
tit.params.dil = True
tit.params.bg = True
tit.params
[9]:
TitrationConfig(bg=True, bg_adj=False, dil=True, nrm=True, bg_mth='mean', mcmc=False)
[10]:
tit.buffer.wells = ["B02"]

tit.buffer.dataframes
[10]:
{1:        B02  Label      fit  fit_err     mean  sem
 0  20707.0      1  20585.5      0.0  20707.0  NaN
 1  20464.0      1  20585.5      0.0  20464.0  NaN,
 2: Empty DataFrame
 Columns: []
 Index: []}
[11]:
tit.bg, tit.bg_err
[11]:
({1: array([1101.43617021, 1088.5106383 ]),
  2: array([628.78787879, 590.15306122])},
 {1: array([nan, nan]), 2: array([nan, nan])})
[12]:
tit.labelblocksgroups[1].data_nrm["A01"]
[12]:
[910.7978723404256, 917.8191489361702]

2.3. Group a list of tecan files into a titration#

The command Titration.fromlistfile(“../listfile”) reads a list of Tecan files, identifies unique measurements in each file, groups matching ones, and combines them into a titration set for further analysis.

[13]:
tit = Titration.fromlistfile("./list.pH.csv", is_ph=True)
print(tit.x)
lbg1 = tit.labelblocksgroups[1]
lbg2 = tit.labelblocksgroups[2]
print(lbg2.labelblocks[6].metadata["Temperature"])
lbg1.metadata, lbg2.metadata
WARNING - Different LabelblocksGroup across files: [PosixPath('290513_8.8.xls'), PosixPath('290513_8.2.xls'), PosixPath('290513_7.7.xls'), PosixPath('290513_7.2.xls'), PosixPath('290513_6.6.xls'), PosixPath('290513_6.1.xls'), PosixPath('290513_5.5.xls')].
[8.9  8.3  7.7  7.05 6.55 6.   5.5 ]
Metadata(value=25.1, unit=['°C'])
[13]:
({'Label': Metadata(value='Label1', unit=None),
  'Mode': Metadata(value='Fluorescence Top Reading', unit=None),
  'Excitation Wavelength': Metadata(value=400, unit=['nm']),
  'Emission Wavelength': Metadata(value=535, unit=['nm']),
  'Excitation Bandwidth': Metadata(value=20, unit=['nm']),
  'Emission Bandwidth': Metadata(value=25, unit=['nm']),
  'Number of Flashes': Metadata(value=10, unit=None),
  'Integration Time': Metadata(value=20, unit=['µs']),
  'Lag Time': Metadata(value='µs', unit=None),
  'Settle Time': Metadata(value='ms', unit=None),
  'Gain': Metadata(value=94, unit=None)},
 {'Label': Metadata(value='Label2', unit=None),
  'Mode': Metadata(value='Fluorescence Top Reading', unit=None),
  'Excitation Wavelength': Metadata(value=485, unit=['nm']),
  'Emission Wavelength': Metadata(value=535, unit=['nm']),
  'Excitation Bandwidth': Metadata(value=25, unit=['nm']),
  'Emission Bandwidth': Metadata(value=25, unit=['nm']),
  'Number of Flashes': Metadata(value=10, unit=None),
  'Integration Time': Metadata(value=20, unit=['µs']),
  'Lag Time': Metadata(value='µs', unit=None),
  'Settle Time': Metadata(value='ms', unit=None),
  'Movement': Metadata(value='Move Plate Out', unit=None)})

Within each labelblockgroups data_norm is immediately calculated.

[14]:
(lbg1.data["H03"], lbg2.data, lbg1.data_nrm["H03"], lbg2.data_nrm["H03"])
[14]:
([27593.0, 26956.0, 26408.0, 26815.0, 28308.0, 30227.0, 30640.0],
 {},
 [1467.712765957447,
  1433.8297872340427,
  1404.6808510638298,
  1426.3297872340427,
  1505.7446808510638,
  1607.8191489361702,
  1629.787234042553],
 [1456.2121212121212,
  1363.9285714285716,
  1310.357142857143,
  1214.5408163265306,
  1200.9693877551022,
  1224.642857142857,
  1193.8265306122448])

Start with platescheme loading to set buffer wells (and consequently buffer values).

Labelblocks group will be populated with data buffer subtracted with/out normalization.

[15]:
tit.load_scheme("./scheme.txt")
print(f"Buffer wells : {tit.scheme.buffer}")
print(f"Ctrl wells   : {tit.scheme.ctrl}")
print(f"CTR name:wells {tit.scheme.names}")

tit.scheme
Buffer wells : ['C12', 'D01', 'D12', 'E01', 'E12', 'F01']
Ctrl wells   : ['C01', 'G12', 'A01', 'B12', 'F12', 'G01', 'A12', 'H01', 'B01', 'H12']
CTR name:wells {'E2GFP': {'C01', 'G01', 'B12', 'F12'}, 'V224L': {'H01', 'A12', 'H12', 'A01'}, 'V224Q': {'B01', 'G12'}}
[15]:
PlateScheme(file='./scheme.txt', _buffer=['C12', 'D01', 'D12', 'E01', 'E12', 'F01'], _discard=[], _ctrl=['C01', 'G12', 'A01', 'B12', 'F12', 'G01', 'A12', 'H01', 'B01', 'H12'], _names={'E2GFP': {'C01', 'G01', 'B12', 'F12'}, 'V224L': {'H01', 'A12', 'H12', 'A01'}, 'V224Q': {'B01', 'G12'}})
[16]:
(lbg1.data["H12"], lbg2.data_nrm["H12"])
[16]:
([28309.0, 27837.0, 26511.0, 25771.0, 27048.0, 27794.0, 28596.0],
 [714.4949494949495,
  686.1224489795918,
  683.3673469387755,
  693.9795918367347,
  737.0408163265306,
  745.765306122449,
  725.8163265306123])
[17]:
tit.load_additions("./additions.pH")
tit.additions
[17]:
[100, 2, 2, 2, 2, 2, 2]
[18]:
(lbg1.data["H12"], tit.data[1]["H12"], lbg1.data_nrm["H12"], tit.bg[1])
[18]:
([28309.0, 27837.0, 26511.0, 25771.0, 27048.0, 27794.0, 28596.0],
 array([302.45567376, 321.23670213, 313.92695035, 277.94929078,
        353.65212766, 335.1001773 , 316.34042553]),
 [1505.7978723404256,
  1480.6914893617022,
  1410.159574468085,
  1370.7978723404256,
  1438.723404255319,
  1478.404255319149,
  1521.063829787234],
 array([1203.34219858, 1165.7535461 , 1108.30673759, 1108.58156028,
        1111.2677305 , 1173.7677305 , 1238.61702128]))

The order in which you apply dilution correction and plate scheme can impact your intermediate results, even though the final results might be the same.

Dilution correction adjusts the measured data to account for any dilutions made during sample preparation. This typically involves multiplying the measured values by the dilution factor to estimate the true concentration of the sample.

A plate scheme describes the layout of the samples on a plate (common in laboratory experiments, such as those involving microtiter plates). The plate scheme may involve rearranging or grouping the data in some way based on the physical location of the samples on the plate.

2.3.1. Reassign Buffer Wells#

You can reassess buffer wells, updating the data to account for any dilution (additions) and subtracting the updated buffer value. This is a handy feature that gives you more control over your analysis.

For instance, consider the following data for a particular well:

[19]:
print(tit.labelblocksgroups[2].data_nrm["D01"])
tit.data[2].get("D01")
[373.6363636363636, 318.6734693877551, 332.60204081632656, 345.0, 364.03061224489795, 375.3571428571429, 401.5816326530612]
[19]:
array([-131.98653199, -151.43877551, -146.51972789, -164.61547619,
       -173.15816327, -229.88690476, -201.95238095])
[20]:
tit.params.bg = False
tit.params.dil = False
print(tit.data[2]["D02"])
[608.23232323 564.03061224 551.2244898  517.19387755 488.92857143
 481.07142857 460.51020408]

You can reassign buffer wells using the buffer_wells attribute:

[21]:
tit.params.bg = True
tit.buffer.wells = ["D01", "E01"]
[22]:
tit.bg
[22]:
{1: array([1016.62234043,  971.56914894,  937.9787234 ,  951.38297872,
         985.74468085, 1005.26595745, 1070.85106383]),
 2: array([420.05050505, 364.69387755, 382.5255102 , 402.19387755,
        430.84183673, 455.07653061, 466.19897959])}

This updates the data for the specified wells, correcting for dilution and subtracting the buffer value:

🚨 The data remain: 🚨

  • unchanged in labelblocksgroups[:].data

    • buffer subtracted in labelblocksgroups[:].data_buffersubtracted

      • buffer subtracted and dilution corrected in data

2.4. Fitting#

test:

  • E10

  • F10

  • G09

TODO:

  • Remove datapoint ini fin outlier

[23]:
os.chdir(data_tests / "L1")
tit = Titration.fromlistfile("./list.pH.csv", is_ph=True)
tit.load_scheme("./scheme.0.txt")
tit.load_additions("additions.pH")
tit
WARNING - Different LabelblocksGroup across files: [PosixPath('290513_8.8.xls'), PosixPath('290513_8.2.xls'), PosixPath('290513_7.7.xls'), PosixPath('290513_7.2.xls'), PosixPath('290513_6.6.xls'), PosixPath('290513_6.1.xls'), PosixPath('290513_5.5.xls')].
[23]:
Titration
        files=["290513_8.8.xls", ...],
        x=[np.float64(8.9), np.float64(8.3), np.float64(7.7), np.float64(7.05), np.float64(6.55), np.float64(6.0), np.float64(5.5)],
        x_err=[np.float64(0.04), np.float64(0.05), np.float64(0.05), np.float64(0.06), np.float64(0.07), np.float64(0.08), np.float64(0.1)],
        labels=dict_keys([1, 2]),
        params=TitrationConfig(bg=True, bg_adj=False, dil=True, nrm=True, bg_mth='mean', mcmc=False)    pH=True additions=[100, 2, 2, 2, 2, 2, 2]
        scheme=PlateScheme(file='./scheme.0.txt', _buffer=['C12', 'D01', 'D12', 'E01', 'E12', 'F01'], _discard=['E10', 'H05'], _ctrl=['C01', 'G12', 'A01', 'B12', 'F12', 'G01', 'A12', 'H01', 'B01', 'H12'], _names={'E2GFP': {'C01', 'G01', 'B12', 'F12'}, 'V224L': {'H01', 'A12', 'H12', 'A01'}, 'V224Q': {'B01', 'G12'}}))
[24]:
tit.results[1].dataframe.head()
[24]:
K sK Khdi03 Khdi97 S0_default sS0_default S0_defaulthdi03 S0_defaulthdi97 S1_default sS1_default S1_defaulthdi03 S1_defaulthdi97
well
H08 3.000002 142.966970 3 11 91.624038 6.849049 -inf inf -13575.030599 6.521788e+06 -inf inf
G08 3.000000 251.785721 3 11 -20.015212 12.447588 -inf inf -20151.194184 1.185301e+07 -inf inf
F02 3.000000 421.490581 3 11 -120.646746 14.976598 -inf inf -16150.260457 1.426103e+07 -inf inf
E04 8.228001 2.553700 3 11 -98.900282 48.492896 -inf inf -72.856228 1.190344e+01 -inf inf
F06 5.078994 1.584982 3 11 121.008841 7.077120 -inf inf -96.162876 5.879950e+02 -inf inf
[25]:
rg = tit.result_global["D10"]
rg.figure
[25]:
../_images/tutorials_prtecan_37_0.png
[26]:
%%time
rro = fitting.fit_binding_pymc_odr(rg, n_sd=0.5)

rro
{'K': K, 'S0_y1': S0_y1, 'S1_y1': S1_y1, 'S0_y2': S0_y2, 'S1_y2': S1_y2}
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [K, S0_y1, S1_y1, S0_y2, S1_y2, ye_mag, xe_mag]
Sampling 4 chains for 1_000 tune and 2_000 draw iterations (4_000 + 8_000 draws total) took 14 seconds.
There were 110 divergences after tuning. Increase `target_accept` or reparameterize.
CPU times: user 38 s, sys: 1.23 s, total: 39.2 s
Wall time: 3min 8s
[26]:
arviz.InferenceData
    • <xarray.Dataset> Size: 3MB
      Dimensions:           (chain: 4, draw: 2000, x_prime_y1_dim_0: 7,
                             y_prime_y1_dim_0: 7, y_model_y1_dim_0: 7,
                             x_prime_y2_dim_0: 7, y_prime_y2_dim_0: 7,
                             y_model_y2_dim_0: 7)
      Coordinates:
        * chain             (chain) int64 32B 0 1 2 3
        * draw              (draw) int64 16kB 0 1 2 3 4 5 ... 1995 1996 1997 1998 1999
        * x_prime_y1_dim_0  (x_prime_y1_dim_0) int64 56B 0 1 2 3 4 5 6
        * y_prime_y1_dim_0  (y_prime_y1_dim_0) int64 56B 0 1 2 3 4 5 6
        * y_model_y1_dim_0  (y_model_y1_dim_0) int64 56B 0 1 2 3 4 5 6
        * x_prime_y2_dim_0  (x_prime_y2_dim_0) int64 56B 0 1 2 3 4 5 6
        * y_prime_y2_dim_0  (y_prime_y2_dim_0) int64 56B 0 1 2 3 4 5 6
        * y_model_y2_dim_0  (y_model_y2_dim_0) int64 56B 0 1 2 3 4 5 6
      Data variables: (12/13)
          K                 (chain, draw) float64 64kB 6.878 6.887 ... 6.871 6.883
          S0_y1             (chain, draw) float64 64kB 185.5 185.8 ... 186.7 186.4
          S1_y1             (chain, draw) float64 64kB 286.4 286.0 ... 284.0 285.9
          S0_y2             (chain, draw) float64 64kB 655.5 656.8 ... 652.2 655.6
          S1_y2             (chain, draw) float64 64kB 221.1 221.0 ... 220.7 220.9
          ye_mag            (chain, draw) float64 64kB 11.78 16.31 8.224 ... 10.96 5.2
          ...                ...
          x_prime_y1        (chain, draw, x_prime_y1_dim_0) float64 448kB 10.34 ......
          y_prime_y1        (chain, draw, y_prime_y1_dim_0) float64 448kB 185.5 ......
          y_model_y1        (chain, draw, y_model_y1_dim_0) float64 448kB 186.4 ......
          x_prime_y2        (chain, draw, x_prime_y2_dim_0) float64 448kB 10.59 ......
          y_prime_y2        (chain, draw, y_prime_y2_dim_0) float64 448kB 655.4 ......
          y_model_y2        (chain, draw, y_model_y2_dim_0) float64 448kB 651.4 ......
      Attributes:
          created_at:                 2025-06-23T11:57:58.111631+00:00
          arviz_version:              0.21.0
          inference_library:          pymc
          inference_library_version:  5.23.0
          sampling_time:              14.180558681488037
          tuning_steps:               1000

    • <xarray.Dataset> Size: 992kB
      Dimensions:                (chain: 4, draw: 2000)
      Coordinates:
        * chain                  (chain) int64 32B 0 1 2 3
        * draw                   (draw) int64 16kB 0 1 2 3 4 ... 1996 1997 1998 1999
      Data variables: (12/17)
          tree_depth             (chain, draw) int64 64kB 3 2 3 2 2 3 ... 3 2 3 3 3 2
          smallest_eigval        (chain, draw) float64 64kB nan nan nan ... nan nan
          perf_counter_diff      (chain, draw) float64 64kB 0.002507 ... 0.000933
          step_size_bar          (chain, draw) float64 64kB 0.8076 0.8076 ... 0.8873
          diverging              (chain, draw) bool 8kB False False ... False False
          step_size              (chain, draw) float64 64kB 0.7661 0.7661 ... 0.7356
          ...                     ...
          reached_max_treedepth  (chain, draw) bool 8kB False False ... False False
          n_steps                (chain, draw) float64 64kB 7.0 3.0 7.0 ... 7.0 3.0
          energy                 (chain, draw) float64 64kB 178.0 176.3 ... 181.8
          max_energy_error       (chain, draw) float64 64kB 0.2141 -0.07371 ... 0.7265
          index_in_trajectory    (chain, draw) int64 64kB -4 2 2 2 2 ... -1 -1 4 -3 -2
          lp                     (chain, draw) float64 64kB -175.9 -175.5 ... -176.4
      Attributes:
          created_at:                 2025-06-23T11:57:58.134155+00:00
          arviz_version:              0.21.0
          inference_library:          pymc
          inference_library_version:  5.23.0
          sampling_time:              14.180558681488037
          tuning_steps:               1000

    • <xarray.Dataset> Size: 224B
      Dimensions:                         (orthogonal_likelihood_y1_dim_0: 7,
                                           orthogonal_likelihood_y2_dim_0: 7)
      Coordinates:
        * orthogonal_likelihood_y1_dim_0  (orthogonal_likelihood_y1_dim_0) int64 56B ...
        * orthogonal_likelihood_y2_dim_0  (orthogonal_likelihood_y2_dim_0) int64 56B ...
      Data variables:
          orthogonal_likelihood_y1        (orthogonal_likelihood_y1_dim_0) float64 56B ...
          orthogonal_likelihood_y2        (orthogonal_likelihood_y2_dim_0) float64 56B ...
      Attributes:
          created_at:                 2025-06-23T11:57:58.140096+00:00
          arviz_version:              0.21.0
          inference_library:          pymc
          inference_library_version:  5.23.0

[27]:
df = az.summary(rro)
df
[27]:
mean sd hdi_3% hdi_97% mcse_mean mcse_sd ess_bulk ess_tail r_hat
K 6.889 0.013 6.864 6.913 0.000 0.000 9868.0 5977.0 1.0
S0_y1 186.141 0.877 184.429 187.709 0.009 0.009 9796.0 5947.0 1.0
S1_y1 286.839 2.213 282.693 290.987 0.022 0.024 9754.0 6073.0 1.0
S0_y2 656.173 2.568 651.243 660.851 0.026 0.028 9523.0 6549.0 1.0
S1_y2 221.087 1.200 218.735 223.252 0.012 0.013 9854.0 6153.0 1.0
ye_mag 10.011 5.779 1.683 20.405 0.061 0.082 8781.0 5923.0 1.0
xe_mag 14.723 0.416 13.953 15.519 0.004 0.005 9112.0 5847.0 1.0
x_prime_y1[0] 10.338 0.000 10.338 10.338 0.000 NaN 8000.0 8000.0 NaN
x_prime_y1[1] 8.431 0.000 8.431 8.431 0.000 0.000 8000.0 8000.0 NaN
x_prime_y1[2] 7.276 0.000 7.276 7.276 0.000 0.000 8000.0 8000.0 NaN
x_prime_y1[3] 7.102 0.000 7.102 7.102 0.000 NaN 8000.0 8000.0 NaN
x_prime_y1[4] 5.213 0.000 5.213 5.213 0.000 NaN 8000.0 8000.0 NaN
x_prime_y1[5] 5.895 0.000 5.895 5.895 0.000 0.000 8000.0 8000.0 NaN
x_prime_y1[6] 6.141 0.000 6.141 6.141 0.000 NaN 8000.0 8000.0 NaN
y_prime_y1[0] 186.177 0.877 184.465 187.744 0.009 0.009 9796.0 5947.0 1.0
y_prime_y1[1] 188.950 0.858 187.344 190.560 0.009 0.009 9822.0 6078.0 1.0
y_prime_y1[2] 215.434 1.093 213.382 217.479 0.011 0.012 10569.0 5995.0 1.0
y_prime_y1[3] 224.373 1.231 222.129 226.744 0.012 0.014 10677.0 5708.0 1.0
y_prime_y1[4] 284.757 2.168 280.811 288.944 0.022 0.024 9804.0 5999.0 1.0
y_prime_y1[5] 277.567 2.025 273.848 281.420 0.020 0.022 9982.0 5962.0 1.0
y_prime_y1[6] 271.575 1.921 268.019 275.162 0.019 0.021 10127.0 5983.0 1.0
y_model_y1[0] 187.113 0.869 185.441 188.687 0.009 0.009 9801.0 5868.0 1.0
y_model_y1[1] 189.902 0.855 188.338 191.552 0.009 0.009 9839.0 6068.0 1.0
y_model_y1[2] 199.612 0.887 197.955 201.312 0.009 0.010 10156.0 6327.0 1.0
y_model_y1[3] 227.242 1.275 224.966 229.746 0.012 0.015 10681.0 5738.0 1.0
y_model_y1[4] 255.176 1.674 252.045 258.252 0.016 0.019 10462.0 6079.0 1.0
y_model_y1[5] 275.311 1.985 271.653 279.053 0.020 0.022 10037.0 6067.0 1.0
y_model_y1[6] 282.884 2.129 279.047 287.026 0.021 0.023 9854.0 5975.0 1.0
x_prime_y2[0] 10.592 0.000 10.592 10.592 0.000 0.000 8000.0 8000.0 NaN
x_prime_y2[1] 8.184 0.000 8.184 8.184 0.000 0.000 8000.0 8000.0 NaN
x_prime_y2[2] 7.689 0.000 7.689 7.689 0.000 NaN 8000.0 8000.0 NaN
x_prime_y2[3] 7.044 0.000 7.044 7.044 0.000 NaN 8000.0 8000.0 NaN
x_prime_y2[4] 6.627 0.000 6.627 6.627 0.000 0.000 8000.0 8000.0 NaN
x_prime_y2[5] 5.934 0.000 5.934 5.934 0.000 0.000 8000.0 8000.0 NaN
x_prime_y2[6] 5.472 0.000 5.472 5.472 0.000 NaN 8000.0 8000.0 NaN
y_prime_y2[0] 656.087 2.568 651.156 660.764 0.026 0.028 9523.0 6549.0 1.0
y_prime_y2[1] 635.172 2.523 630.453 639.922 0.026 0.027 9326.0 6033.0 1.0
y_prime_y2[2] 596.694 2.731 591.694 601.883 0.028 0.030 9237.0 6324.0 1.0
y_prime_y2[3] 477.078 3.613 470.356 483.735 0.037 0.040 9548.0 6146.0 1.0
y_prime_y2[4] 375.072 3.294 369.041 381.264 0.033 0.036 9725.0 5998.0 1.0
y_prime_y2[5] 264.596 1.657 261.512 267.749 0.016 0.018 10094.0 6393.0 1.0
y_prime_y2[6] 237.154 1.265 234.772 239.533 0.013 0.014 10204.0 6441.0 1.0
y_model_y2[0] 651.974 2.547 647.181 656.702 0.026 0.028 9469.0 6467.0 1.0
y_model_y2[1] 639.925 2.521 635.413 644.867 0.026 0.027 9353.0 6177.0 1.0
y_model_y2[2] 597.970 2.720 592.793 602.936 0.028 0.030 9234.0 6337.0 1.0
y_model_y2[3] 478.588 3.609 471.885 485.232 0.037 0.040 9544.0 6159.0 1.0
y_model_y2[4] 357.894 3.117 351.892 363.504 0.032 0.034 9749.0 5905.0 1.0
y_model_y2[5] 270.899 1.767 267.594 274.239 0.018 0.019 10018.0 6393.0 1.0
y_model_y2[6] 238.178 1.275 235.715 240.514 0.013 0.014 10213.0 6383.0 1.0
[28]:
az.plot_trace(rro)
[28]:
array([[<Axes: title={'center': 'K'}>, <Axes: title={'center': 'K'}>],
       [<Axes: title={'center': 'S0_y1'}>,
        <Axes: title={'center': 'S0_y1'}>],
       [<Axes: title={'center': 'S1_y1'}>,
        <Axes: title={'center': 'S1_y1'}>],
       [<Axes: title={'center': 'S0_y2'}>,
        <Axes: title={'center': 'S0_y2'}>],
       [<Axes: title={'center': 'S1_y2'}>,
        <Axes: title={'center': 'S1_y2'}>],
       [<Axes: title={'center': 'ye_mag'}>,
        <Axes: title={'center': 'ye_mag'}>],
       [<Axes: title={'center': 'xe_mag'}>,
        <Axes: title={'center': 'xe_mag'}>],
       [<Axes: title={'center': 'x_prime_y1'}>,
        <Axes: title={'center': 'x_prime_y1'}>],
       [<Axes: title={'center': 'y_prime_y1'}>,
        <Axes: title={'center': 'y_prime_y1'}>],
       [<Axes: title={'center': 'y_model_y1'}>,
        <Axes: title={'center': 'y_model_y1'}>],
       [<Axes: title={'center': 'x_prime_y2'}>,
        <Axes: title={'center': 'x_prime_y2'}>],
       [<Axes: title={'center': 'y_prime_y2'}>,
        <Axes: title={'center': 'y_prime_y2'}>],
       [<Axes: title={'center': 'y_model_y2'}>,
        <Axes: title={'center': 'y_model_y2'}>]], dtype=object)
../_images/tutorials_prtecan_40_1.png
[29]:
rp = tit.result_mcmc["H11"]
2025-06-23 11:58:04,815 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:04,991 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:07,378 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:07,638 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:08,339 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:08,600 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:08,688 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:08,871 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:09,651 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:09,822 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:10,093 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:11,589 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
2025-06-23 11:58:11,676 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO - n_sd[Global] estimated for MCMC fitting: 2.997
INFO - Starting PyMC sampling for key: H11
2025-06-23 11:58:12,810 - clophfit.binding.fitting - INFO - min pH distance: 0.2695113885676778
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [K, S0_y1, S1_y1, S0_y2, S1_y2, x_diff, x_start, ye_mag]
Sampling 4 chains for 1_000 tune and 2_000 draw iterations (4_000 + 8_000 draws total) took 30 seconds.
INFO - MCMC fitting completed for well: H11
Parameters([('K', <Parameter 'K', value=np.float64(6.885) +/- 0.098, bounds=[np.float64(6.706):np.float64(7.076)]>), ('S0_y1', <Parameter 'S0_y1', value=np.float64(390.417) +/- 9.02, bounds=[np.float64(373.323):np.float64(407.452)]>), ('S1_y1', <Parameter 'S1_y1', value=np.float64(394.837) +/- 8.3, bounds=[np.float64(379.262):np.float64(410.696)]>), ('S0_y2', <Parameter 'S0_y2', value=np.float64(1467.393) +/- 18.2, bounds=[np.float64(1433.126):np.float64(1501.667)]>), ('S1_y2', <Parameter 'S1_y2', value=np.float64(503.375) +/- 20.8, bounds=[np.float64(463.248):np.float64(540.741)]>)])
{'K': <Parameter 'K', value=np.float64(6.885) +/- 0.098, bounds=[np.float64(6.706):np.float64(7.076)]>, 'S0': <Parameter 'S0_y2', value=np.float64(1467.393) +/- 18.2, bounds=[np.float64(1433.126):np.float64(1501.667)]>, 'S1': <Parameter 'S1_y2', value=np.float64(503.375) +/- 20.8, bounds=[np.float64(463.248):np.float64(540.741)]>}
[30]:
rp.figure
[30]:
../_images/tutorials_prtecan_42_0.png
[31]:
rp.dataset
[31]:
{'y1': DataArray(xc=array([8.898, 8.29 , 7.695, 7.058, 6.615, 6.04 , 5.517]), yc=array([364.79609929, 400.93776596, 397.68014184, 363.4822695 ,
        436.72021277, 391.79698582, 394.91914894]), x_errc=array([0.039, 0.07 , 0.086, 0.093, 0.108, 0.118, 0.177]), y_errc=array([19.49947799, 16.97439118, 11.5031079 , 29.7615832 , 38.95086938,
        11.89263627, 10.50281031]), _mask=array([ True,  True,  True,  True,  True,  True,  True])),
 'y2': DataArray(xc=array([8.898, 8.29 , 7.695, 7.058, 6.615, 6.04 , 5.517]), yc=array([1498.46801347, 1419.41326531, 1334.94965986, 1076.7202381 ,
         864.1377551 ,  625.64370748,  541.24761905]), x_errc=array([0.039, 0.07 , 0.086, 0.093, 0.108, 0.118, 0.177]), y_errc=array([33.96866131, 21.29558784, 17.37632337, 19.48394992, 35.96392592,
         6.5403459 , 15.97286261]), _mask=array([ True,  True,  True,  True,  True,  True,  True]))}
[32]:
az.plot_trace(
    rp.mini, var_names=["x_true", "K", "x_diff"], divergences=False, combined=True
)
[32]:
array([[<Axes: title={'center': 'x_true'}>,
        <Axes: title={'center': 'x_true'}>],
       [<Axes: title={'center': 'K'}>, <Axes: title={'center': 'K'}>],
       [<Axes: title={'center': 'x_diff'}>,
        <Axes: title={'center': 'x_diff'}>]], dtype=object)
../_images/tutorials_prtecan_44_1.png
[33]:
type(tit.result_global["H11"].result.params.keys())
[33]:
dict_keys
[34]:
tit.result_global.all_computed()
[34]:
True

2.4.1. Choose buffer value to be subtracted between mean values and ODR fitted values.#

[35]:
lb = 2

x = tit.buffer.dataframes_nrm[lb]["fit"]
y = tit.buffer.dataframes_nrm[lb]["mean"]
x_err = tit.buffer.dataframes_nrm[lb]["fit_err"] / 10
y_err = tit.buffer.dataframes_nrm[lb]["sem"] / 10

plt.errorbar(
    x,
    y,
    xerr=x_err,
    yerr=y_err,
    fmt="o",
    color="blue",
    ecolor="lightgray",
    elinewidth=2,
    capsize=4,
)
plt.xlabel("ODR Fit")
plt.ylabel("Buffer wells Mean")
[35]:
Text(0, 0.5, 'Buffer wells Mean')
../_images/tutorials_prtecan_48_1.png
[36]:
tit.buffer.plot(1).fig
[36]:
../_images/tutorials_prtecan_49_0.png
[37]:
tit.buffer.fit_results_nrm
[37]:
{1: BufferFit(m=np.float64(-7.306402487135711), q=np.float64(1204.3701797583942), m_err=np.float64(19.39191342345837), q_err=np.float64(137.90570601875348)),
 2: BufferFit(m=np.float64(-29.420727413380433), q=np.float64(728.3516672997897), m_err=np.float64(10.519439216261752), q_err=np.float64(77.32603244223898))}
[38]:
tit.plot_temperature()
[38]:
../_images/tutorials_prtecan_51_0.png
[39]:
tit.bg_err
[39]:
{1: array([ 96.7021583 , 101.34977225,  85.42617933,  81.13557359,
         77.67092652,  79.89850564,  92.9287542 ]),
 2: array([52.85291426, 57.60633679, 52.2150539 , 54.27412064, 53.73263201,
        59.45449329, 68.93471729])}
[40]:
k = "F10"  # "G09"
tit.result_global[k].figure
[40]:
../_images/tutorials_prtecan_53_0.png
[41]:
r = tit.result_global[k]
r.result.params
[41]:
[42]:
tit.result_odr[k].figure
[42]:
../_images/tutorials_prtecan_55_0.png
[43]:
ro = fitting.fit_binding_odr(r)
ro.figure
# ro.result.params
[43]:
../_images/tutorials_prtecan_56_0.png
[44]:
tit._dil_corr
[44]:
array([1.  , 1.02, 1.04, 1.06, 1.08, 1.1 , 1.12])
[45]:
tit.params.nrm = False
tit.params
[45]:
TitrationConfig(bg=True, bg_adj=False, dil=True, nrm=False, bg_mth='mean', mcmc=False)
[46]:
os.chdir("../../Tecan/140220/")
tit = Titration.fromlistfile("./list.pH.csv", is_ph=True)
tit.load_scheme("./scheme.txt")
tit.load_additions("additions.pH")
WARNING -  OVER value in Label1: H02 of tecanfile pH7.6_200214.xls
WARNING -  OVER value in Label1: H02 of tecanfile pH7.1_200214.xls
WARNING -  OVER value in Label1: A06 of tecanfile pH6.5_200214.xls
WARNING -  OVER value in Label1: H02 of tecanfile pH6.5_200214.xls
WARNING -  OVER value in Label1: A06 of tecanfile pH5.8_200214.xls
WARNING -  OVER value in Label1: H02 of tecanfile pH5.8_200214.xls
WARNING -  OVER value in Label1: H02 of tecanfile pH5.0_200214.xls
[47]:
tit.data[1]["H03"]
[47]:
array([ 449.81182796,  654.84274194, 1000.00752688, 1218.41586022,
       1298.01774194, 1313.15456989,  977.35053763])
[48]:
tit.params.bg_adj = True
tit.params.bg_mth = "mean"

tit.params
[48]:
TitrationConfig(bg=True, bg_adj=True, dil=True, nrm=True, bg_mth='mean', mcmc=False)
tit.params.mcmc = True tit.result_mcmc
[49]:
df1 = pd.read_csv("../140220/fit1-1.csv", index_col=0)
# merged_df = tit.result_dfs[1][["K", "sK"]].merge(df1, left_index=True, right_index=True)
merged_df = (
    tit.results[2].dataframe[["K", "sK"]].merge(df1, left_index=True, right_index=True)
)

sb.jointplot(merged_df, x="K_y", y="K_x", ratio=3, space=0.4)
WARNING - Buffer for 'G03:2' was adjusted by 293.02 SD.
[49]:
<seaborn.axisgrid.JointGrid at 0x7fb11aad52b0>
../_images/tutorials_prtecan_63_2.png
[50]:
tit.result_global["A01"].figure
[50]:
../_images/tutorials_prtecan_64_0.png
[51]:
tit.data[1]["A01"]
[51]:
array([210.34946237, 324.65887097, 492.19677419, 556.48575269,
       640.20483871, 602.59005376, 585.10967742])

If a fit fails in a well, the well key will be anyway present in results list of dict.

tit.results[1].compute_all() conf = prtecan.TecanConfig(Path("jjj"), False, (), "", True, True) tit.export_data_fit(conf)
[52]:
print(tit.data[1]["H02"])
tit.results[2]["H02"].figure
[1865.34946237 2636.32983871           nan           nan           nan
           nan           nan]
[52]:
../_images/tutorials_prtecan_68_1.png
[53]:
print(tit.results[2].results.keys() - tit.results[2].results.keys())
print(tit.result_global.results.keys() - tit.results[1].results.keys())
print(tit.result_odr.results.keys() - tit.results[1].results.keys())
set()
{'A01'}
set()
[54]:
tit.params.nrm = False
[55]:
tit.results[1]["H01"].figure
WARNING - Buffer for 'G03:2' was adjusted by 293.02 SD.
[55]:
../_images/tutorials_prtecan_71_1.png
[56]:
tit.result_global["H01"].figure
2025-06-23 11:59:20,096 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
[56]:
../_images/tutorials_prtecan_72_1.png

And in the global fit (i.e. fitting 2 labelblocks) dataset with insufficient data points are removed.

[57]:
tit.params.nrm = True
well = "H02"
y1 = np.array(tit.data[1][well])
y2 = np.array(tit.data[2][well])

x = np.array(tit.x)
ds = fitting.Dataset(
    {"y1": fitting.DataArray(x, y1), "y2": fitting.DataArray(x, y2)}, is_ph=True
)
rfit = fitting.fit_binding_glob(ds)

rfit.result.params
WARNING - Buffer for 'G03:2' was adjusted by 293.02 SD.
[57]:
[58]:
rfit.figure
[58]:
../_images/tutorials_prtecan_75_0.png
[59]:
fitting.fit_binding_odr(rfit).figure
[59]:
../_images/tutorials_prtecan_76_0.png
[60]:
tit.results[2].dataframe.head()
[60]:
K sK Khdi03 Khdi97 S0_default sS0_default S0_defaulthdi03 S0_defaulthdi97 S1_default sS1_default S1_defaulthdi03 S1_defaulthdi97
well
H08 8.038281 0.017818 3 11 2344.941764 21.704171 -inf inf 136.318919 14.080618 -inf inf
G08 8.043590 0.031946 3 11 317.415028 5.415529 -inf inf 10.538279 3.490372 -inf inf
F02 7.873810 0.006420 3 11 671.767126 2.165047 -inf inf 31.164596 1.689769 -inf inf
E04 7.974377 0.021112 3 11 921.813896 10.087450 -inf inf 39.252165 7.059981 -inf inf
F06 7.879647 0.010925 3 11 302.002738 1.630850 -inf inf 18.905793 1.265196 -inf inf

You can decide how to pre-process data with datafit_params:

  • [bg] subtract background

  • [dil] apply correction for dilution (when e.g. during a titration you add titrant without protein)

  • [nrm] normalize for gain, number of flashes and integration time.

[61]:
tit.params.nrm = False
tit.params.bg = True
tit.params.bg_adj = False
tit.data[1]["E06"]
[61]:
array([ 7069.5  , 10878.555, 16151.98 , 19465.575, 20140.65 , 19205.175,
       13086.08 ])

2.4.2. Posterior analysis with emcee#

To explore the posterior of parameters you can use the Minimizer object returned in FitResult.

[62]:
np.random.seed(0)  # noqa: NPY002
remcee = rfit.mini.emcee(
    burn=50,
    steps=2000,
    workers=8,
    thin=10,
    nwalkers=30,
    progress=False,
    is_weighted=False,
)
The chain is shorter than 50 times the integrated autocorrelation time for 6 parameter(s). Use this estimate with caution and run a longer chain!
N/50 = 40;
tau: [111.06922308 191.80894426 171.41194937  75.9163616   60.79653061
 236.46411446]
[63]:
f = plotting.plot_emcee(remcee.flatchain)
print(remcee.flatchain.quantile([0.03, 0.97])["K"].to_list())
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
WARNING:root:Too few points to create valid contours
[7.6501941591783575, 7.977095194607985]
../_images/tutorials_prtecan_82_2.png
[64]:
samples = remcee.flatchain[["K"]]
# Convert the dictionary of flatchains to an ArviZ InferenceData object
samples_dict = {key: np.array(val) for key, val in samples.items()}
idata = az.from_dict(posterior=samples_dict)
k_samples = idata.posterior["K"].to_numpy()
percentile_value = np.percentile(k_samples, 3)
print(f"Value at which the probability of being higher is 99%: {percentile_value}")

az.plot_forest(k_samples)
Value at which the probability of being higher is 99%: 7.6501941591783575
[64]:
array([<Axes: title={'center': '94.0% HDI'}>], dtype=object)
../_images/tutorials_prtecan_83_2.png

2.4.3. Cl titration analysis#

[65]:
os.chdir("../140220/")
cl_an = prtecan.Titration.fromlistfile("list.cl.csv", is_ph=False)
cl_an.load_scheme("scheme.txt")
cl_an.scheme
WARNING -  OVER value in Label1: H02 of tecanfile pH5.0_200214.xls
WARNING:clophfit.prtecan.prtecan: OVER value in Label1: H02 of tecanfile pH5.0_200214.xls
[65]:
PlateScheme(file='scheme.txt', _buffer=['D01', 'E01', 'D12', 'E12'], _discard=[], _ctrl=['C01', 'F01', 'G12', 'A01', 'B12', 'F12', 'H01', 'G01', 'A12', 'B01', 'C12', 'H12'], _names={'G03': {'B12', 'H12', 'A01'}, 'NTT': {'C12', 'F01', 'F12'}, 'S202N': {'C01', 'H01', 'G12'}, 'V224Q': {'B01', 'G01', 'A12'}})
[66]:
cl_an.load_additions("additions.cl")
print(cl_an.x)
cl_an.x = prtecan.calculate_conc(cl_an.additions, 1000)
cl_an.x
[0 0 0 0 0 0 0 0 0]
[66]:
array([  0.        ,  17.54385965,  34.48275862,  50.84745763,
        66.66666667,  81.96721311,  96.77419355, 138.46153846,
       164.17910448])
[67]:
fres = cl_an.result_global[well]
print(fres.is_valid(), fres.result.bic, fres.result.redchi)
fres.figure
True 9.10198424235326 1.0517121862661403
[67]:
../_images/tutorials_prtecan_87_1.png

2.4.4. Plotting#

[68]:
tit.results[2].plot_k(title="2014-12-23")
[68]:
../_images/tutorials_prtecan_89_0.png

2.4.5. selection#

[69]:
tit.params.nrm = True
tit.params.dil = True
tit.params.bg_mth = "fit"
tit
[69]:
Titration
        files=["pH9.1_200214.xls", ...],
        x=[np.float64(9.0633), np.float64(8.35), np.float64(7.7), np.float64(7.08), np.float64(6.44), np.float64(5.83), np.float64(4.99)],
        x_err=[np.float64(0.0115), np.float64(0.02), np.float64(0.08), np.float64(0.03), np.float64(0.0872), np.float64(0.1), np.float64(0.0361)],
        labels=dict_keys([1, 2]),
        params=TitrationConfig(bg=True, bg_adj=False, dil=True, nrm=True, bg_mth='fit', mcmc=False)     pH=True additions=[100, 2, 2, 2, 2, 2, 2]
        scheme=PlateScheme(file='./scheme.txt', _buffer=['D01', 'E01', 'D12', 'E12'], _discard=[], _ctrl=['C01', 'F01', 'G12', 'A01', 'B12', 'F12', 'H01', 'G01', 'A12', 'B01', 'C12', 'H12'], _names={'G03': {'B12', 'H12', 'A01'}, 'NTT': {'C12', 'F01', 'F12'}, 'S202N': {'C01', 'H01', 'G12'}, 'V224Q': {'B01', 'G01', 'A12'}}))
[70]:
df_ctr = tit.results[1].dataframe
for name, wells in tit.scheme.names.items():
    for well in wells:
        df_ctr.loc[well, "ctrl"] = name

df_ctr.loc[df_ctr["ctrl"].isna(), "ctrl"] = "U"

sb.set_style("whitegrid")
g = sb.PairGrid(
    df_ctr,
    x_vars=["K", "S1_default", "S0_default"],
    y_vars=["K", "S1_default", "S0_default"],
    hue="ctrl",
    palette="Set1",
    diag_sharey=False,
)
g.map_lower(plt.scatter)
g.map_upper(sb.kdeplot, fill=True)
g.map_diag(sb.kdeplot)
g.add_legend()
WARNING - Skip fit for well H02 for Label:1.
WARNING:clophfit.prtecan.prtecan:Skip fit for well H02 for Label:1.
[70]:
<seaborn.axisgrid.PairGrid at 0x7fb11aad6510>
../_images/tutorials_prtecan_92_2.png
[71]:
with sb.axes_style("darkgrid"):
    g = sb.pairplot(
        tit.result_global.dataframe[["S1_y2", "S0_y2", "K", "S1_y1", "S0_y1"]],
        hue="S1_y1",
        palette="Reds",
        corner=True,
        diag_kind="kde",
    )
2025-06-23 11:59:41,041 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:41,133 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:41,224 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:41,313 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:41,486 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:41,666 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:41,754 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:41,930 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:42,196 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:42,943 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:43,301 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:43,822 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:43,996 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:44,176 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:44,436 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:44,528 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:44,617 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:44,702 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:44,875 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:45,138 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:45,492 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:45,580 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:45,754 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:45,844 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:45,936 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:46,200 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:46,467 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:46,643 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:47,446 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:47,714 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:48,151 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:48,325 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:48,600 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:48,865 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:48,956 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:49,046 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:49,396 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:49,493 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:49,753 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:49,842 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
2025-06-23 11:59:50,358 - clophfit.binding.fitting - INFO - Outlier(s) in well Label:.
INFO:clophfit.binding.fitting:Outlier(s) in well Label:.
../_images/tutorials_prtecan_93_1.png

2.4.6. combining#

[72]:
keys_unk = tit.fit_keys - set(tit.scheme.ctrl)
res_unk = tit.results[1].dataframe.loc[list(keys_unk)].sort_index()
res_unk["well"] = res_unk.index
[73]:
f = plt.figure(figsize=(24, 14))
# Make the PairGrid
g = sb.PairGrid(
    res_unk,
    x_vars=["K", "S1_default", "S0_default"],
    y_vars="well",
    height=12,
    aspect=0.4,
)
# Draw a dot plot using the stripplot function
g.map(sb.stripplot, size=14, orient="h", palette="Set2", edgecolor="auto")

# Use the same x axis limits on all columns and add better labels
# g.set(xlim=(0, 25), xlabel="Crashes", ylabel="")

# Use semantically meaningful titles for the columns
titles = ["$pK_a$", "B$_{neutral}$", "B$_{anionic}$"]

for ax, title in zip(g.axes.flat, titles, strict=False):
    # Set a different title for each axes
    ax.set(title=title)

    # Make the grid horizontal instead of vertical
    ax.xaxis.grid(False)
    ax.yaxis.grid(True)

sb.despine(left=True, bottom=True)
<Figure size 2400x1400 with 0 Axes>
../_images/tutorials_prtecan_96_1.png