In [1]:
import pandas as pd
import numpy as np
import statsmodels.api as sm
import statsmodels.formula.api as smf
import scipy.stats as stats
from statsmodels.stats.multitest import multipletests


import matplotlib.pyplot as plt
import seaborn as sns

from skimage import filters, morphology, measure, color
import aicsimageio as aics

import glob

plt.rcParams['pdf.fonttype'] = 'truetype'
plt.rcParams['font.size'] = 20
plt.style.use('seaborn-v0_8-ticks')
In [2]:
color_palette = {'DMSO' : '#4d4d4d',
                 'ARYL_AG':'#F8766D', 'ARYL_INH':'#F8766D50',
                 'ANDR_AG':'#fccb17', 'ANDR_INH':'#C49A0050',  
                 "ESTR_AG": '#53B400', "ESTR_INH": '#53B40050', 
                 'GC_AG' : '#00C094', 'GC_INH' : '#00C09450',
                 'LX_AG' : '#00B6EB', 'LX_INH' : '#00B6EB50', 
                 'RET_AG' : '#A58AFF', 'RET_INH' : '#A58AFF50', 
                 'THYR_AG' : '#FB61D7', 'THYR_INH' : '#FB61D750'
}

line_palette = {
    'CTL08A': "#99cfd670",  
    'CTL04E': "#f8c88a70",  
}
In [3]:
color_palette_expanded = {'DMSO_CTL08A': '#cacaca',
 'DMSO_CTL04E': '#4d4d4d',
 'ARYL_AG_CTL08A': '#fdd6d3',
 'ARYL_AG_CTL04E': '#F8766D',
 'ARYL_INH_CTL08A': '#fdd6d3',
 'ARYL_INH_CTL04E': '#F8766D50',
 'ANDR_AG_CTL08A': '#feefb9',
 'ANDR_AG_CTL04E': '#fccb17',
 'ANDR_INH_CTL08A': '#ede1b250',
 'ANDR_INH_CTL04E': '#C49A0050',
 'ESTR_AG_CTL08A': '#cbe8b2',
 'ESTR_AG_CTL04E': '#53B400',
 'ESTR_INH_CTL08A': '#cbe8b250',
 'ESTR_INH_CTL04E': '#53B40050',
 'GC_AG_CTL08A': '#b2ecdf',
 'GC_AG_CTL04E': '#00C094',
 'GC_INH_CTL08A': '#b2ecdf50',
 'GC_INH_CTL04E': '#00C09450',
 'LX_AG_CTL08A': '#b2e9f9',
 'LX_AG_CTL04E': '#00B6EB',
 'LX_INH_CTL08A': '#b2e9f950',
 'LX_INH_CTL04E': '#00B6EB50',
 'RET_AG_CTL08A': '#e4dcff',
 'RET_AG_CTL04E': '#A58AFF',
 'RET_INH_CTL08A': '#e4dcff50',
 'RET_INH_CTL04E': '#A58AFF50',
 'THYR_AG_CTL08A': '#fed0f3',
 'THYR_AG_CTL04E': '#FB61D7',
 'THYR_INH_CTL08A': '#fed0f350',
 'THYR_INH_CTL04E': '#FB61D750'}
In [4]:
hormonal_conditions = ['ESTR_AG', 'ESTR_INH', 'ANDR_AG', 'ANDR_INH', 'ARYL_INH',
       'ARYL_AG', 'LX_AG', 'GC_AG', 'LX_INH', 'GC_INH', 'THYR_INH',
       'THYR_AG', 'DMSO', 'RET_AG',
       'RET_INH']
hormonal_conditions.remove('DMSO')
hormonal_conditions.sort()
hormonal_conditions = ['DMSO'] + hormonal_conditions

Load manual checks¶

In [5]:
blank_mix1 = glob.glob('/group/testa/Project/EndPoints/TPSSU/analysis/single_tif_scanregion/*0136*.tif')
blank_mix1 = [f.split('/')[-1].split('.')[0] for f in blank_mix1]
blank_mix2 = glob.glob('/group/testa/Project/EndPoints/TPSSU/analysis/single_tif_scanregion/*0145*.tif')
blank_mix2 = [f.split('/')[-1].split('.')[0] for f in blank_mix2]
In [6]:
check_qc_ch1 = pd.read_csv('../../preprocessing/tables/Treshold_QC_check - Channel1_no_dup.csv')
check_qc_ch2 = pd.read_csv('../../preprocessing/tables/Treshold_QC_check - Channel2_no_dup.csv')
In [7]:
check_qc_ch1['general QC (keep or not)'] = check_qc_ch1['general QC (keep or not)'].str.upper()
check_qc_ch1['general QC (keep or not)'] = check_qc_ch1['general QC (keep or not)'].replace({'BETTER NOT': 'LIMIT', 'MAYBE': 'LIMIT', 'YES?': 'LIMIT', 'SUS': 'LIMIT', 'NOT SURE': 'LIMIT', 'YES ': 'YES', 'YES - 2 CBO': 'YES'})
check_qc_ch1['general QC (keep or not)'].value_counts().index
Out[7]:
Index(['YES', 'LIMIT', 'NO'], dtype='object', name='general QC (keep or not)')
In [8]:
check_qc_ch1['Reference Image Report'] = check_qc_ch1['Reference Image Report'].str.replace('.pdf', '')
In [9]:
check_qc_ch1['Reference Image Report'].value_counts()
Out[9]:
Reference Image Report
20240424_manuel_lessi_0148_ScanRegion1_rep_1_channel_1    1
20240424_manuel_lessi_0145_ScanRegion2_rep_2_channel_1    1
20240424_manuel_lessi_0145_ScanRegion3_rep_0_channel_1    1
20240424_manuel_lessi_0145_ScanRegion3_rep_1_channel_1    1
20240424_manuel_lessi_0145_ScanRegion3_rep_2_channel_1    1
                                                         ..
20240424_manuel_lessi_0136_ScanRegion2_rep_1_channel_1    1
20240424_manuel_lessi_0136_ScanRegion2_rep_2_channel_1    1
20240424_manuel_lessi_0136_ScanRegion3_rep_0_channel_1    1
20240424_manuel_lessi_0136_ScanRegion3_rep_1_channel_1    1
20240424_manuel_lessi_0136_ScanRegion3_rep_2_channel_1    1
Name: count, Length: 153, dtype: int64
In [10]:
check_qc_ch2['general QC (keep or not)'] = check_qc_ch2['general QC (keep or not)'].str.upper()
check_qc_ch2['general QC (keep or not)'] = check_qc_ch2['general QC (keep or not)'].replace({'NOT SURE': 'LIMIT', '?': 'LIMIT', 'YES ': 'YES'})
check_qc_ch2['general QC (keep or not)'].value_counts().index
Out[10]:
Index(['YES', 'NO', 'LIMIT'], dtype='object', name='general QC (keep or not)')
In [11]:
check_qc_ch2['Reference Image Report'] = check_qc_ch2['Reference Image Report'].str.replace('.pdf', '')
In [12]:
check_qc_ch1['Reference Image Report'].value_counts()
Out[12]:
Reference Image Report
20240424_manuel_lessi_0148_ScanRegion1_rep_1_channel_1    1
20240424_manuel_lessi_0145_ScanRegion2_rep_2_channel_1    1
20240424_manuel_lessi_0145_ScanRegion3_rep_0_channel_1    1
20240424_manuel_lessi_0145_ScanRegion3_rep_1_channel_1    1
20240424_manuel_lessi_0145_ScanRegion3_rep_2_channel_1    1
                                                         ..
20240424_manuel_lessi_0136_ScanRegion2_rep_1_channel_1    1
20240424_manuel_lessi_0136_ScanRegion2_rep_2_channel_1    1
20240424_manuel_lessi_0136_ScanRegion3_rep_0_channel_1    1
20240424_manuel_lessi_0136_ScanRegion3_rep_1_channel_1    1
20240424_manuel_lessi_0136_ScanRegion3_rep_2_channel_1    1
Name: count, Length: 153, dtype: int64
In [13]:
check_qc_ch1['ImageName_ScanRegion_Rep'] = check_qc_ch1['Reference Image Report'].apply(lambda x: '_'.join(x.split('_')[:7]))
check_qc_ch2['ImageName_ScanRegion_Rep'] = check_qc_ch2['Reference Image Report'].apply(lambda x: '_'.join(x.split('_')[:7]))

to_keep_ch1 = check_qc_ch1[check_qc_ch1['general QC (keep or not)'].isin(['YES', 'LIMIT'])]
to_keep_ch2 = check_qc_ch2[check_qc_ch2['general QC (keep or not)'].isin(['YES', 'LIMIT'])]

len(set(to_keep_ch1['ImageName_ScanRegion_Rep']).intersection(set(to_keep_ch2['ImageName_ScanRegion_Rep'])))
Out[13]:
100
In [14]:
len(check_qc_ch1['ImageName_ScanRegion_Rep'])
Out[14]:
153
In [15]:
len(check_qc_ch2['ImageName_ScanRegion_Rep'])
Out[15]:
275
In [16]:
good_tissue_to_keep_limit = list(set(check_qc_ch1[check_qc_ch1['general QC (keep or not)'].isin(['YES', 'LIMIT'])]['ImageName_ScanRegion_Rep']).union(set(check_qc_ch2[check_qc_ch2['general QC (keep or not)'].isin(['YES', 'LIMIT'])]['ImageName_ScanRegion_Rep'])))
In [17]:
len(good_tissue_to_keep_limit)
Out[17]:
245
In [18]:
good_tissue_to_keep_limit_df = pd.DataFrame(good_tissue_to_keep_limit, columns=['ImageName_ScanRegion_Rep'])
In [19]:
good_tissue_to_keep_limit_df['ImageName_ScanRegion'] = good_tissue_to_keep_limit_df['ImageName_ScanRegion_Rep'].apply(lambda x: '_'.join(x.split('_')[:5]))
good_tissue_to_keep_limit_df
Out[19]:
ImageName_ScanRegion_Rep ImageName_ScanRegion
0 20240424_manuel_lessi_0147_ScanRegion1_rep_1 20240424_manuel_lessi_0147_ScanRegion1
1 20240424_manuel_lessi_0139_ScanRegion4_rep_1 20240424_manuel_lessi_0139_ScanRegion4
2 20240424_manuel_lessi_0132_ScanRegion3_rep_2 20240424_manuel_lessi_0132_ScanRegion3
3 20240424_manuel_lessi_0138_ScanRegion0_rep_1 20240424_manuel_lessi_0138_ScanRegion0
4 20240424_manuel_lessi_0129_ScanRegion1_rep_2 20240424_manuel_lessi_0129_ScanRegion1
... ... ...
240 20240424_manuel_lessi_0132_ScanRegion4_rep_1 20240424_manuel_lessi_0132_ScanRegion4
241 20240424_manuel_lessi_0135_ScanRegion0_rep_2 20240424_manuel_lessi_0135_ScanRegion0
242 20240424_manuel_lessi_0141_ScanRegion4_rep_1 20240424_manuel_lessi_0141_ScanRegion4
243 20240424_manuel_lessi_0129_ScanRegion2_rep_0 20240424_manuel_lessi_0129_ScanRegion2
244 20240424_manuel_lessi_0132_ScanRegion1_rep_0 20240424_manuel_lessi_0132_ScanRegion1

245 rows × 2 columns

Load sample sheet¶

In [20]:
sample_sheet  = pd.read_csv('../../preprocessing/tables/ImageAnalysisSampleSheet.csv')
In [21]:
sample_sheet = sample_sheet[sample_sheet['Condition'].isin(hormonal_conditions)]
In [22]:
sample_sheet['Condition'] = sample_sheet['Condition'].str.replace('_ANT', '_INH')
In [23]:
sample_sheet['Condition'].unique()
Out[23]:
array(['ESTR_AG', 'ESTR_INH', 'ANDR_AG', 'ARYL_INH', 'ARYL_AG', 'LX_AG',
       'GC_AG', 'LX_INH', 'GC_INH', 'THYR_INH', 'THYR_AG', 'DMSO',
       'RET_AG', 'RET_INH', 'ANDR_INH'], dtype=object)
In [24]:
sample_sheet['ImageName'] = sample_sheet['Path'].apply(lambda x: x.split('\\')[-1].split('.')[0])
sample_sheet['ImageName_ScanRegion'] = sample_sheet['ImageName'] + '_' + sample_sheet['Scan Area']
sample_sheet['Condition_Line'] = sample_sheet['Condition'] + '_' + sample_sheet['Line ']
sample_sheet
Out[24]:
Path Scan Area Condition Line OPA Paraffin Block ID MIX ImageName ImageName_ScanRegion Condition_Line
0 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 ESTR_AG CTL04E 12 29 1 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion0 ESTR_AG_CTL04E
1 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion1 ESTR_AG CTL08A 12 30 1 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion1 ESTR_AG_CTL08A
2 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 ESTR_INH CTL04E 12 31 1 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion2 ESTR_INH_CTL04E
3 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion3 ANDR_AG CTL04E 12 33 1 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion3 ANDR_AG_CTL04E
4 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 ESTR_INH CTL08A 12 32 1 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 ESTR_INH_CTL08A
5 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ANDR_AG CTL08A 12 34 1 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 ANDR_AG_CTL08A
7 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion1 ARYL_INH CTL08A 13 40 1 20240424_manuel_lessi_0129 20240424_manuel_lessi_0129_ScanRegion1 ARYL_INH_CTL08A
8 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 ARYL_INH CTL04E 13 39 1 20240424_manuel_lessi_0129 20240424_manuel_lessi_0129_ScanRegion2 ARYL_INH_CTL04E
9 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion3 ARYL_AG CTL08A 13 38 1 20240424_manuel_lessi_0129 20240424_manuel_lessi_0129_ScanRegion3 ARYL_AG_CTL08A
11 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ARYL_AG CTL04E 13 37 1 20240424_manuel_lessi_0129 20240424_manuel_lessi_0129_ScanRegion5 ARYL_AG_CTL04E
12 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 LX_AG CTL04E 14 41 1 20240424_manuel_lessi_0130 20240424_manuel_lessi_0130_ScanRegion0 LX_AG_CTL04E
13 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion1 GC_AG CTL08A 14 46 1 20240424_manuel_lessi_0130 20240424_manuel_lessi_0130_ScanRegion1 GC_AG_CTL08A
14 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 LX_AG CTL08A 14 42 1 20240424_manuel_lessi_0130 20240424_manuel_lessi_0130_ScanRegion2 LX_AG_CTL08A
15 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion3 LX_INH CTL04E 14 43 1 20240424_manuel_lessi_0130 20240424_manuel_lessi_0130_ScanRegion3 LX_INH_CTL04E
16 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 LX_INH CTL08A 14 44 1 20240424_manuel_lessi_0130 20240424_manuel_lessi_0130_ScanRegion4 LX_INH_CTL08A
17 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 GC_INH CTL04E 15 47 1 20240424_manuel_lessi_0131 20240424_manuel_lessi_0131_ScanRegion0 GC_INH_CTL04E
18 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion1 THYR_INH CTL04E 15 51 1 20240424_manuel_lessi_0131 20240424_manuel_lessi_0131_ScanRegion1 THYR_INH_CTL04E
19 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 THYR_AG CTL08A 15 50 1 20240424_manuel_lessi_0131 20240424_manuel_lessi_0131_ScanRegion2 THYR_AG_CTL08A
20 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion3 GC_INH CTL08A 15 48 1 20240424_manuel_lessi_0131 20240424_manuel_lessi_0131_ScanRegion3 GC_INH_CTL08A
21 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 THYR_AG CTL04E 15 49 1 20240424_manuel_lessi_0131 20240424_manuel_lessi_0131_ScanRegion4 THYR_AG_CTL04E
22 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 THYR_INH CTL08A 15 52 1 20240424_manuel_lessi_0131 20240424_manuel_lessi_0131_ScanRegion5 THYR_INH_CTL08A
23 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 DMSO CTL04E 16 57 1 20240424_manuel_lessi_0132 20240424_manuel_lessi_0132_ScanRegion4 DMSO_CTL04E
24 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 DMSO CTL08A 16 58 1 20240424_manuel_lessi_0132 20240424_manuel_lessi_0132_ScanRegion5 DMSO_CTL08A
25 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 RET_AG CTL08A 17 64 1 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion4 RET_AG_CTL08A
26 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 RET_INH CTL04E 18 65 1 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 RET_INH_CTL04E
27 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 RET_INH CTL08A 18 66 1 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion2 RET_INH_CTL08A
28 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 ESTR_AG CTL04E 12 29 2 20240424_manuel_lessi_0137 20240424_manuel_lessi_0137_ScanRegion0 ESTR_AG_CTL04E
29 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion1 ANDR_AG CTL08A 12 34 2 20240424_manuel_lessi_0137 20240424_manuel_lessi_0137_ScanRegion1 ANDR_AG_CTL08A
30 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 ESTR_AG CTL08A 12 30 2 20240424_manuel_lessi_0137 20240424_manuel_lessi_0137_ScanRegion2 ESTR_AG_CTL08A
31 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion3 ESTR_INH CTL04E 12 31 2 20240424_manuel_lessi_0137 20240424_manuel_lessi_0137_ScanRegion3 ESTR_INH_CTL04E
32 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 ANDR_AG CTL04E 12 33 2 20240424_manuel_lessi_0137 20240424_manuel_lessi_0137_ScanRegion4 ANDR_AG_CTL04E
33 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ESTR_INH CTL08A 12 32 2 20240424_manuel_lessi_0137 20240424_manuel_lessi_0137_ScanRegion5 ESTR_INH_CTL08A
34 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 ANDR_INH CTL08A 13 36 2 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 ANDR_INH_CTL08A
35 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion1 ANDR_INH CTL04E 13 35 2 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion1 ANDR_INH_CTL04E
36 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 ARYL_INH CTL04E 13 39 2 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion2 ARYL_INH_CTL04E
37 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion3 ARYL_AG CTL08A 13 38 2 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion3 ARYL_AG_CTL08A
38 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 ARYL_INH CTL08A 13 40 2 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion4 ARYL_INH_CTL08A
39 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ARYL_AG CTL04E 13 37 2 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion5 ARYL_AG_CTL04E
40 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 LX_AG CTL08A 14 42 2 20240424_manuel_lessi_0139 20240424_manuel_lessi_0139_ScanRegion0 LX_AG_CTL08A
41 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion1 LX_INH CTL08A 14 44 2 20240424_manuel_lessi_0139 20240424_manuel_lessi_0139_ScanRegion1 LX_INH_CTL08A
42 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 LX_AG CTL04E 14 41 2 20240424_manuel_lessi_0139 20240424_manuel_lessi_0139_ScanRegion2 LX_AG_CTL04E
43 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion3 LX_INH CTL04E 14 43 2 20240424_manuel_lessi_0139 20240424_manuel_lessi_0139_ScanRegion3 LX_INH_CTL04E
44 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 GC_AG CTL08A 14 46 2 20240424_manuel_lessi_0139 20240424_manuel_lessi_0139_ScanRegion4 GC_AG_CTL08A
45 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 GC_INH CTL08A 15 48 2 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion0 GC_INH_CTL08A
46 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion1 GC_INH CTL04E 15 47 2 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion1 GC_INH_CTL04E
47 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 THYR_INH CTL04E 15 51 2 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion2 THYR_INH_CTL04E
48 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion3 THYR_AG CTL04E 15 49 2 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion3 THYR_AG_CTL04E
49 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 THYR_INH CTL08A 15 52 2 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion4 THYR_INH_CTL08A
50 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 THYR_AG CTL08A 15 50 2 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion5 THYR_AG_CTL08A
51 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 DMSO CTL08A 16 58 2 20240424_manuel_lessi_0141 20240424_manuel_lessi_0141_ScanRegion2 DMSO_CTL08A
52 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion3 DMSO CTL04E 16 57 2 20240424_manuel_lessi_0141 20240424_manuel_lessi_0141_ScanRegion3 DMSO_CTL04E
53 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion2 RET_AG CTL08A 17 64 2 20240424_manuel_lessi_0142 20240424_manuel_lessi_0142_ScanRegion2 RET_AG_CTL08A
54 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 RET_INH CTL08A 18 66 2 20240424_manuel_lessi_0143 20240424_manuel_lessi_0143_ScanRegion0 RET_INH_CTL08A
55 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion1 RET_INH CTL04E 18 65 2 20240424_manuel_lessi_0143 20240424_manuel_lessi_0143_ScanRegion1 RET_INH_CTL04E
In [25]:
sample_dict= {i:j for i, j in zip(sample_sheet['ImageName_ScanRegion'], sample_sheet['Condition_Line'])}
In [26]:
mix_1_names = sample_sheet[sample_sheet['MIX'] == 1]['ImageName'].tolist()
mix_2_names = sample_sheet[sample_sheet['MIX'] == 2]['ImageName'].tolist()
In [27]:
sample_sheet['Condition'].unique()
Out[27]:
array(['ESTR_AG', 'ESTR_INH', 'ANDR_AG', 'ARYL_INH', 'ARYL_AG', 'LX_AG',
       'GC_AG', 'LX_INH', 'GC_INH', 'THYR_INH', 'THYR_AG', 'DMSO',
       'RET_AG', 'RET_INH', 'ANDR_INH'], dtype=object)

Load measurements file names¶

In [28]:
measurements_files = glob.glob("/group/testa/Project/EndPoints/TPSSU/analysis/measurements/*.csv")
measurements_files_mix_1 = [f for f in measurements_files if any(name in f for name in mix_1_names)]
measurements_files_mix_2 = [f for f in measurements_files if any(name in f for name in mix_2_names)]
In [29]:
mask_file = pd.read_csv("/group/testa/Project/EndPoints/TPSSU/analysis/cytoplasmatic_measures/cyto_mask_measures.csv", index_col=0)
mask_file
Out[29]:
level_0 level_1 area label intensity_mean intensity_min intensity_max
0 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_0_c... 41353242.0 1 2417.670006 1451.0 24120.0
1 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_1_c... 16657221.0 1 3091.305189 1683.0 18027.0
2 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_2_c... 44113333.0 1 3377.073590 1493.0 27952.0
3 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion1_rep_0_c... 10098868.0 1 2645.539375 1110.0 27468.0
4 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion1_rep_1_c... 7030992.0 1 3937.014572 1941.0 25144.0
... ... ... ... ... ... ... ...
275 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion2_rep_1_c... 23124873.0 1 4259.092122 2134.0 44177.0
276 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion2_rep_2_c... 3373772.0 1 5777.220379 2861.0 33473.0
277 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion3_rep_0_c... 10606818.0 1 6209.021854 3028.0 52453.0
278 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion3_rep_1_c... 27412715.0 1 3358.864704 1478.0 28030.0
279 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion3_rep_2_c... 34550995.0 1 3780.418198 1524.0 49946.0

280 rows × 7 columns

In [30]:
mask_file['OriginalImageName'] = mask_file['level_1'].apply(lambda x: '_'.join(x.split('_')[0:4]))
mask_file['ImageName_ScanRegion'] = mask_file['level_1'].apply(lambda x: '_'.join(x.split('_')[0:5]))
mask_file['ImageName_ScanRegion_Rep'] = mask_file['level_1'].apply(lambda x: '_'.join(x.split('_')[0:7]))
mask_file
Out[30]:
level_0 level_1 area label intensity_mean intensity_min intensity_max OriginalImageName ImageName_ScanRegion ImageName_ScanRegion_Rep
0 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_0_c... 41353242.0 1 2417.670006 1451.0 24120.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_0
1 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_1_c... 16657221.0 1 3091.305189 1683.0 18027.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_1
2 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_2_c... 44113333.0 1 3377.073590 1493.0 27952.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_2
3 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion1_rep_0_c... 10098868.0 1 2645.539375 1110.0 27468.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion1 20240424_manuel_lessi_0134_ScanRegion1_rep_0
4 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion1_rep_1_c... 7030992.0 1 3937.014572 1941.0 25144.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion1 20240424_manuel_lessi_0134_ScanRegion1_rep_1
... ... ... ... ... ... ... ... ... ... ...
275 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion2_rep_1_c... 23124873.0 1 4259.092122 2134.0 44177.0 20240424_manuel_lessi_0144 20240424_manuel_lessi_0144_ScanRegion2 20240424_manuel_lessi_0144_ScanRegion2_rep_1
276 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion2_rep_2_c... 3373772.0 1 5777.220379 2861.0 33473.0 20240424_manuel_lessi_0144 20240424_manuel_lessi_0144_ScanRegion2 20240424_manuel_lessi_0144_ScanRegion2_rep_2
277 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion3_rep_0_c... 10606818.0 1 6209.021854 3028.0 52453.0 20240424_manuel_lessi_0144 20240424_manuel_lessi_0144_ScanRegion3 20240424_manuel_lessi_0144_ScanRegion3_rep_0
278 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion3_rep_1_c... 27412715.0 1 3358.864704 1478.0 28030.0 20240424_manuel_lessi_0144 20240424_manuel_lessi_0144_ScanRegion3 20240424_manuel_lessi_0144_ScanRegion3_rep_1
279 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0144_ScanRegion3_rep_2_c... 34550995.0 1 3780.418198 1524.0 49946.0 20240424_manuel_lessi_0144 20240424_manuel_lessi_0144_ScanRegion3 20240424_manuel_lessi_0144_ScanRegion3_rep_2

280 rows × 10 columns

In [31]:
mask_file_df_mix_1 = mask_file[mask_file.OriginalImageName.isin(mix_1_names)]
mask_file_df_mix_1
Out[31]:
level_0 level_1 area label intensity_mean intensity_min intensity_max OriginalImageName ImageName_ScanRegion ImageName_ScanRegion_Rep
0 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_0_c... 41353242.0 1 2417.670006 1451.0 24120.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_0
1 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_1_c... 16657221.0 1 3091.305189 1683.0 18027.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_1
2 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_2_c... 44113333.0 1 3377.073590 1493.0 27952.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_2
3 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion1_rep_0_c... 10098868.0 1 2645.539375 1110.0 27468.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion1 20240424_manuel_lessi_0134_ScanRegion1_rep_0
4 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion1_rep_1_c... 7030992.0 1 3937.014572 1941.0 25144.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion1 20240424_manuel_lessi_0134_ScanRegion1_rep_1
... ... ... ... ... ... ... ... ... ... ...
253 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion4_rep_0_c... 31563732.0 1 5826.471794 2338.0 65535.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_0
254 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion4_rep_1_c... 15937774.0 1 4450.546162 1907.0 34600.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_1
255 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_0_c... 27625720.0 1 8458.966141 3148.0 65535.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_0
256 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_1_c... 24213189.0 1 8076.440269 3291.0 64452.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_1
257 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_2_c... 40133175.0 1 4824.591142 1926.0 50550.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_2

115 rows × 10 columns

In [32]:
mask_file_df_mix_2 = mask_file[mask_file.OriginalImageName.isin(mix_2_names)]
mask_file_df_mix_2
Out[32]:
level_0 level_1 area label intensity_mean intensity_min intensity_max OriginalImageName ImageName_ScanRegion ImageName_ScanRegion_Rep
63 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_0_c... 25216880.0 1 2897.342427 1595.0 46841.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 20240424_manuel_lessi_0138_ScanRegion0_rep_0
64 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_1_c... 783450.0 1 5758.093586 2353.0 28128.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 20240424_manuel_lessi_0138_ScanRegion0_rep_1
65 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_2_c... 9406872.0 1 2427.271453 1434.0 22097.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 20240424_manuel_lessi_0138_ScanRegion0_rep_2
66 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion1_rep_0_c... 19764267.0 1 4312.851140 1931.0 46239.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion1 20240424_manuel_lessi_0138_ScanRegion1_rep_0
67 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion1_rep_1_c... 16905853.0 1 5440.278854 1960.0 53194.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion1 20240424_manuel_lessi_0138_ScanRegion1_rep_1
... ... ... ... ... ... ... ... ... ... ...
225 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion4_rep_1_c... 31837996.0 1 3904.760504 1749.0 59013.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion4 20240424_manuel_lessi_0140_ScanRegion4_rep_1
226 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion4_rep_2_c... 18872105.0 1 3218.488803 1461.0 31570.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion4 20240424_manuel_lessi_0140_ScanRegion4_rep_2
227 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion5_rep_0_c... 26246594.0 1 5094.892965 2251.0 65058.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion5 20240424_manuel_lessi_0140_ScanRegion5_rep_0
228 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion5_rep_1_c... 1893364.0 1 10712.595047 4554.0 42993.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion5 20240424_manuel_lessi_0140_ScanRegion5_rep_1
229 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion5_rep_2_c... 24276964.0 1 3060.892932 1587.0 48985.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion5 20240424_manuel_lessi_0140_ScanRegion5_rep_2

108 rows × 10 columns

In [33]:
SOX2_KI67_mask_file = pd.read_csv("/group/testa/Project/EndPoints/TPSSU/analysis/SOX2_signal_mask_measures/SOX2_mask_measures.csv", index_col=0)
SOX2_KI67_mask_file
Out[33]:
level_0 level_1 area label intensity_mean intensity_min intensity_max
0 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_0_c... 4101511.0 1 3193.621243 760.0 58222.0
1 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_1_c... 364102.0 1 1737.696456 799.0 32870.0
2 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_2_c... 1211261.0 1 2790.889845 666.0 55530.0
3 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion1_rep_0_c... 5725447.0 1 2689.380828 667.0 65535.0
4 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion1_rep_1_c... 3492983.0 1 2311.704404 652.0 65535.0
... ... ... ... ... ... ... ...
275 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion0_rep_0_c... 28772264.0 1 2396.070626 1210.0 46386.0
276 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion0_rep_1_c... 65092576.0 1 1752.924324 920.0 65535.0
277 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion1_rep_0_c... 22241252.0 1 2317.127770 873.0 63830.0
278 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion1_rep_1_c... 21330335.0 1 2837.997036 1092.0 65535.0
279 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion1_rep_2_c... 28381819.0 1 1679.860053 669.0 57590.0

280 rows × 7 columns

In [34]:
SOX2_KI67_mask_file['OriginalImageName'] = SOX2_KI67_mask_file['level_1'].apply(lambda x: '_'.join(x.split('_')[0:4]))
SOX2_KI67_mask_file['ImageName_ScanRegion'] = SOX2_KI67_mask_file['level_1'].apply(lambda x: '_'.join(x.split('_')[0:5]))
SOX2_KI67_mask_file['ImageName_ScanRegion_Rep'] = SOX2_KI67_mask_file['level_1'].apply(lambda x: '_'.join(x.split('_')[0:7]))
SOX2_KI67_mask_file
Out[34]:
level_0 level_1 area label intensity_mean intensity_min intensity_max OriginalImageName ImageName_ScanRegion ImageName_ScanRegion_Rep
0 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_0_c... 4101511.0 1 3193.621243 760.0 58222.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 20240424_manuel_lessi_0138_ScanRegion0_rep_0
1 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_1_c... 364102.0 1 1737.696456 799.0 32870.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 20240424_manuel_lessi_0138_ScanRegion0_rep_1
2 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_2_c... 1211261.0 1 2790.889845 666.0 55530.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 20240424_manuel_lessi_0138_ScanRegion0_rep_2
3 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion1_rep_0_c... 5725447.0 1 2689.380828 667.0 65535.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion1 20240424_manuel_lessi_0138_ScanRegion1_rep_0
4 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion1_rep_1_c... 3492983.0 1 2311.704404 652.0 65535.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion1 20240424_manuel_lessi_0138_ScanRegion1_rep_1
... ... ... ... ... ... ... ... ... ... ...
275 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion0_rep_0_c... 28772264.0 1 2396.070626 1210.0 46386.0 20240424_manuel_lessi_0147 20240424_manuel_lessi_0147_ScanRegion0 20240424_manuel_lessi_0147_ScanRegion0_rep_0
276 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion0_rep_1_c... 65092576.0 1 1752.924324 920.0 65535.0 20240424_manuel_lessi_0147 20240424_manuel_lessi_0147_ScanRegion0 20240424_manuel_lessi_0147_ScanRegion0_rep_1
277 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion1_rep_0_c... 22241252.0 1 2317.127770 873.0 63830.0 20240424_manuel_lessi_0147 20240424_manuel_lessi_0147_ScanRegion1 20240424_manuel_lessi_0147_ScanRegion1_rep_0
278 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion1_rep_1_c... 21330335.0 1 2837.997036 1092.0 65535.0 20240424_manuel_lessi_0147 20240424_manuel_lessi_0147_ScanRegion1 20240424_manuel_lessi_0147_ScanRegion1_rep_1
279 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0147_ScanRegion1_rep_2_c... 28381819.0 1 1679.860053 669.0 57590.0 20240424_manuel_lessi_0147 20240424_manuel_lessi_0147_ScanRegion1 20240424_manuel_lessi_0147_ScanRegion1_rep_2

280 rows × 10 columns

In [35]:
SOX2_KI67_mask_file_df_mix_1 = SOX2_KI67_mask_file[SOX2_KI67_mask_file.OriginalImageName.isin(mix_1_names)]
SOX2_KI67_mask_file_df_mix_1
Out[35]:
level_0 level_1 area label intensity_mean intensity_min intensity_max OriginalImageName ImageName_ScanRegion ImageName_ScanRegion_Rep
19 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion0_rep_0_c... 21490093.0 1 3839.355973 1168.0 38274.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion0 20240424_manuel_lessi_0133_ScanRegion0_rep_0
20 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion0_rep_1_c... 14199055.0 1 4477.197476 1226.0 46860.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion0 20240424_manuel_lessi_0133_ScanRegion0_rep_1
21 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion0_rep_2_c... 15755642.0 1 3759.473544 1092.0 43784.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion0 20240424_manuel_lessi_0133_ScanRegion0_rep_2
22 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion1_rep_0_c... 1037489.0 1 2031.997964 957.0 21498.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion1 20240424_manuel_lessi_0133_ScanRegion1_rep_0
23 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion1_rep_1_c... 2023185.0 1 3780.785030 1512.0 38018.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion1 20240424_manuel_lessi_0133_ScanRegion1_rep_1
... ... ... ... ... ... ... ... ... ... ...
270 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion4_rep_0_c... 16244663.0 1 4090.208776 1313.0 48739.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_0
271 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion4_rep_1_c... 7057712.0 1 4113.493459 1235.0 42141.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_1
272 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_0_c... 12963431.0 1 5394.798945 1568.0 44306.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_0
273 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_1_c... 10831979.0 1 4610.981995 1604.0 40226.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_1
274 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_2_c... 17819211.0 1 3234.373585 1320.0 37653.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_2

115 rows × 10 columns

In [36]:
SOX2_KI67_mask_file_df_mix_2 = SOX2_KI67_mask_file[SOX2_KI67_mask_file.OriginalImageName.isin(mix_2_names)]
SOX2_KI67_mask_file_df_mix_2
Out[36]:
level_0 level_1 area label intensity_mean intensity_min intensity_max OriginalImageName ImageName_ScanRegion ImageName_ScanRegion_Rep
0 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_0_c... 4101511.0 1 3193.621243 760.0 58222.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 20240424_manuel_lessi_0138_ScanRegion0_rep_0
1 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_1_c... 364102.0 1 1737.696456 799.0 32870.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 20240424_manuel_lessi_0138_ScanRegion0_rep_1
2 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion0_rep_2_c... 1211261.0 1 2790.889845 666.0 55530.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion0 20240424_manuel_lessi_0138_ScanRegion0_rep_2
3 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion1_rep_0_c... 5725447.0 1 2689.380828 667.0 65535.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion1 20240424_manuel_lessi_0138_ScanRegion1_rep_0
4 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0138_ScanRegion1_rep_1_c... 3492983.0 1 2311.704404 652.0 65535.0 20240424_manuel_lessi_0138 20240424_manuel_lessi_0138_ScanRegion1 20240424_manuel_lessi_0138_ScanRegion1_rep_1
... ... ... ... ... ... ... ... ... ... ...
239 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion4_rep_1_c... 7722247.0 1 1513.283052 548.0 65125.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion4 20240424_manuel_lessi_0140_ScanRegion4_rep_1
240 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion4_rep_2_c... 3527916.0 1 2265.226677 660.0 65535.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion4 20240424_manuel_lessi_0140_ScanRegion4_rep_2
241 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion5_rep_0_c... 10066105.0 1 972.838009 474.0 65535.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion5 20240424_manuel_lessi_0140_ScanRegion5_rep_0
242 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion5_rep_1_c... 416818.0 1 2934.184800 745.0 65535.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion5 20240424_manuel_lessi_0140_ScanRegion5_rep_1
243 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0140_ScanRegion5_rep_2_c... 4548954.0 1 1941.690161 621.0 52428.0 20240424_manuel_lessi_0140 20240424_manuel_lessi_0140_ScanRegion5 20240424_manuel_lessi_0140_ScanRegion5_rep_2

108 rows × 10 columns

Area organoids¶

In [37]:
organoid_areas = pd.read_csv('/group/testa/Project/EndPoints/TPSSU/analysis/organoids_area.csv', index_col=0)
organoid_areas
Out[37]:
area
20240424_manuel_lessi_0141_ScanRegion1_rep_0 48618374
20240424_manuel_lessi_0131_ScanRegion0_rep_2 36008220
20240424_manuel_lessi_0133_ScanRegion4_rep_0 8422522
20240424_manuel_lessi_0143_ScanRegion2_rep_1 61481135
20240424_manuel_lessi_0147_ScanRegion0_rep_1 162545379
... ...
20240424_manuel_lessi_0129_ScanRegion1_rep_0 26572184
20240424_manuel_lessi_0129_ScanRegion0_rep_0 9292198
20240424_manuel_lessi_0142_ScanRegion1_rep_2 38017446
20240424_manuel_lessi_0129_ScanRegion2_rep_1 29459387
20240424_manuel_lessi_0134_ScanRegion4_rep_1 22517768

280 rows × 1 columns

In [38]:
organoid_areas['ImageName_ScanRegion'] = organoid_areas.reset_index()['index'].apply(lambda x: '_'.join(x.split('_')[0:5])).values
organoid_areas['Condition_Line'] = organoid_areas['ImageName_ScanRegion'].map(sample_dict)
organoid_areas['Line'] = organoid_areas['Condition_Line'].apply(lambda x: x.split('_')[-1] if pd.notnull(x) else x)
organoid_areas['Condition'] = organoid_areas['Condition_Line'].str.replace('_CTL08A', '', regex=True)
organoid_areas['Condition'] = organoid_areas['Condition'].str.replace('_CTL04E', '', regex=True)
organoid_areas = organoid_areas[organoid_areas['Condition'].isin(hormonal_conditions)]
organoid_areas
Out[38]:
area ImageName_ScanRegion Condition_Line Line Condition
20240424_manuel_lessi_0131_ScanRegion0_rep_2 36008220 20240424_manuel_lessi_0131_ScanRegion0 GC_INH_CTL04E CTL04E GC_INH
20240424_manuel_lessi_0133_ScanRegion4_rep_0 8422522 20240424_manuel_lessi_0133_ScanRegion4 RET_AG_CTL08A CTL08A RET_AG
20240424_manuel_lessi_0140_ScanRegion4_rep_2 32680044 20240424_manuel_lessi_0140_ScanRegion4 THYR_INH_CTL08A CTL08A THYR_INH
20240424_manuel_lessi_0140_ScanRegion0_rep_1 36008064 20240424_manuel_lessi_0140_ScanRegion0 GC_INH_CTL08A CTL08A GC_INH
20240424_manuel_lessi_0138_ScanRegion3_rep_1 16957648 20240424_manuel_lessi_0138_ScanRegion3 ARYL_AG_CTL08A CTL08A ARYL_AG
... ... ... ... ... ...
20240424_manuel_lessi_0131_ScanRegion4_rep_1 35725975 20240424_manuel_lessi_0131_ScanRegion4 THYR_AG_CTL04E CTL04E THYR_AG
20240424_manuel_lessi_0140_ScanRegion3_rep_0 46296832 20240424_manuel_lessi_0140_ScanRegion3 THYR_AG_CTL04E CTL04E THYR_AG
20240424_manuel_lessi_0128_ScanRegion4_rep_0 42198648 20240424_manuel_lessi_0128_ScanRegion4 ESTR_INH_CTL08A CTL08A ESTR_INH
20240424_manuel_lessi_0129_ScanRegion1_rep_0 26572184 20240424_manuel_lessi_0129_ScanRegion1 ARYL_INH_CTL08A CTL08A ARYL_INH
20240424_manuel_lessi_0129_ScanRegion2_rep_1 29459387 20240424_manuel_lessi_0129_ScanRegion2 ARYL_INH_CTL04E CTL04E ARYL_INH

146 rows × 5 columns

In [39]:
sample_sheet[~sample_sheet['ImageName_ScanRegion'].isin(organoid_areas['ImageName_ScanRegion'].tolist())]
Out[39]:
Path Scan Area Condition Line OPA Paraffin Block ID MIX ImageName ImageName_ScanRegion Condition_Line
In [40]:
img = aics.AICSImage('/group/testa/Project/EndPoints/TPSSU/Pictures/20240424_manuel_lessi_0134.czi')
px_size = img.physical_pixel_sizes.X
px_size
Out[40]:
0.325
In [41]:
organoid_areas['Area_um2'] = organoid_areas['area'] * (px_size ** 2)
/tmp/ipykernel_359582/3605050963.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas['Area_um2'] = organoid_areas['area'] * (px_size ** 2)
In [42]:
to_plot = organoid_areas.loc[[i for i in good_tissue_to_keep_limit if i in organoid_areas.index]]
to_plot = to_plot[['Condition', 'Line']].value_counts().reset_index()
plt.figure(figsize = (5, 7))
sns.barplot(to_plot, x='count', y='Condition', hue='Line', order = hormonal_conditions, palette=line_palette)
plt.title('Number of organoids per condition')
plt.legend(title='Line', bbox_to_anchor=(1.05, 1), loc='upper left')

plt.savefig('../../../../endpoints_figures/organoid_count_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/organoid_count_per_condition.png', dpi=300, bbox_inches='tight')
In [43]:
fig, ax = plt.subplots(1, 2, figsize=(20, 5))
organoid_areas = organoid_areas[organoid_areas['Condition'].isin(hormonal_conditions)]
for i, cond in enumerate(hormonal_conditions):
    data = organoid_areas[(organoid_areas['Line'] == 'CTL08A') & 
                                 (organoid_areas['Condition'] == cond)]['Area_um2']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = organoid_areas[(organoid_areas['Condition'] == 'DMSO') & 
                                       (organoid_areas['Line'] == 'CTL08A')]['Area_um2'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=organoid_areas[(organoid_areas['Line'] == 'CTL08A')],
    y='Area_um2', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = organoid_areas[(organoid_areas['Line'] == 'CTL04E') & 
                                 (organoid_areas['Condition'] == cond)]['Area_um2']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = organoid_areas[(organoid_areas['Condition'] == 'DMSO') & 
                                       (organoid_areas['Line'] == 'CTL04E')]['Area_um2'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=organoid_areas[organoid_areas['Line'] == 'CTL04E'],
    y='Area_um2', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('Area (um²)')
ax[1].set_ylabel('Area (um²)')

plt.savefig('../../../../endpoints_figures/endpoints_organoids_area_grouped.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/endpoints_organoids_area_grouped.png', dpi=300, bbox_inches='tight')

Statistical test - Area¶

In [44]:
organoid_areas_CTL08A = organoid_areas[organoid_areas['Line'] == 'CTL08A']
organoid_areas_CTL08A['Condition'] = organoid_areas_CTL08A['Condition'].astype('category')
organoid_areas_CTL08A["log_area"] = np.log(organoid_areas_CTL08A["Area_um2"])
organoid_areas_CTL08A['log_area'].hist(bins = 10)

model_CTL08A = smf.ols("log_area ~ C(Condition, Treatment(reference='DMSO'))",
                data=organoid_areas_CTL08A,
                ).fit()

print(model_CTL08A.summary())
/tmp/ipykernel_359582/2873068395.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL08A['Condition'] = organoid_areas_CTL08A['Condition'].astype('category')
/tmp/ipykernel_359582/2873068395.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL08A["log_area"] = np.log(organoid_areas_CTL08A["Area_um2"])
                            OLS Regression Results                            
==============================================================================
Dep. Variable:               log_area   R-squared:                       0.418
Model:                            OLS   Adj. R-squared:                  0.296
Method:                 Least Squares   F-statistic:                     3.431
Date:                Thu, 05 Mar 2026   Prob (F-statistic):           0.000333
Time:                        11:17:32   Log-Likelihood:                -73.462
No. Observations:                  82   AIC:                             176.9
Df Residuals:                      67   BIC:                             213.0
Df Model:                          14                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                15.4856      0.268     57.849      0.000      14.951      16.020
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]     -0.2767      0.379     -0.731      0.467      -1.032       0.479
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]    -1.7082      0.464     -3.684      0.000      -2.634      -0.783
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]     -0.7963      0.346     -2.304      0.024      -1.486      -0.107
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]    -1.1674      0.379     -3.084      0.003      -1.923      -0.412
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]     -1.4736      0.423     -3.482      0.001      -2.318      -0.629
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]    -0.5383      0.423     -1.272      0.208      -1.383       0.306
C(Condition, Treatment(reference='DMSO'))[T.GC_AG]       -0.6476      0.423     -1.530      0.131      -1.492       0.197
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]      -0.5371      0.379     -1.419      0.161      -1.293       0.219
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]       -1.1601      0.379     -3.065      0.003      -1.916      -0.405
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]      -0.8513      0.423     -2.011      0.048      -1.696      -0.006
C(Condition, Treatment(reference='DMSO'))[T.RET_AG]      -1.6444      0.379     -4.344      0.000      -2.400      -0.889
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]     -0.4029      0.379     -1.064      0.291      -1.158       0.353
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]     -1.1685      0.379     -3.087      0.003      -1.924      -0.413
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]    -0.1595      0.379     -0.421      0.675      -0.915       0.596
==============================================================================
Omnibus:                       13.242   Durbin-Watson:                   1.918
Prob(Omnibus):                  0.001   Jarque-Bera (JB):               20.705
Skew:                          -0.637   Prob(JB):                     3.19e-05
Kurtosis:                       5.107   Cond. No.                         15.3
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [45]:
organoid_areas_CTL04E = organoid_areas[organoid_areas['Line'] == 'CTL04E']
organoid_areas_CTL04E['Condition'] = organoid_areas_CTL04E['Condition'].astype('category')
organoid_areas_CTL04E["log_area"] = np.log(organoid_areas_CTL04E["Area_um2"])
organoid_areas_CTL04E['log_area'].hist(bins = 10)

model_CTL04E = smf.ols("log_area ~ C(Condition, Treatment(reference='DMSO'))",
                data=organoid_areas_CTL04E,
                ).fit()

print(model_CTL04E.summary())
/tmp/ipykernel_359582/1765289553.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL04E['Condition'] = organoid_areas_CTL04E['Condition'].astype('category')
/tmp/ipykernel_359582/1765289553.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL04E["log_area"] = np.log(organoid_areas_CTL04E["Area_um2"])
                            OLS Regression Results                            
==============================================================================
Dep. Variable:               log_area   R-squared:                       0.528
Model:                            OLS   Adj. R-squared:                  0.416
Method:                 Least Squares   F-statistic:                     4.746
Date:                Thu, 05 Mar 2026   Prob (F-statistic):           3.77e-05
Time:                        11:17:32   Log-Likelihood:                -41.911
No. Observations:                  64   AIC:                             109.8
Df Residuals:                      51   BIC:                             137.9
Df Model:                          12                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                14.7172      0.261     56.414      0.000      14.193      15.241
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]      0.0188      0.369      0.051      0.959      -0.722       0.760
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]     0.2327      0.398      0.584      0.562      -0.567       1.033
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]      1.0591      0.337      3.145      0.003       0.383       1.735
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]    -0.0609      0.337     -0.181      0.857      -0.737       0.615
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]      0.5125      0.398      1.286      0.204      -0.287       1.313
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]     0.5022      0.337      1.491      0.142      -0.174       1.178
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]       0.5818      0.337      1.728      0.090      -0.094       1.258
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]       -0.3924      0.337     -1.165      0.249      -1.069       0.284
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]       0.7487      0.337      2.223      0.031       0.073       1.425
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]      0.6445      0.337      1.914      0.061      -0.032       1.321
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]      0.4704      0.337      1.397      0.169      -0.206       1.147
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]    -1.2498      0.452     -2.766      0.008      -2.157      -0.343
==============================================================================
Omnibus:                        7.906   Durbin-Watson:                   1.913
Prob(Omnibus):                  0.019   Jarque-Bera (JB):                7.916
Skew:                          -0.605   Prob(JB):                       0.0191
Kurtosis:                       4.226   Cond. No.                         15.5
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [46]:
resid_CTL08A = model_CTL08A.resid

stats.probplot(resid_CTL08A, dist="norm", plot=plt)
plt.show()
In [47]:
resid_CTL04E = model_CTL04E.resid

stats.probplot(resid_CTL04E, dist="norm", plot=plt)
plt.show()
In [48]:
pvals_CTL08A = model_CTL08A.pvalues[1:]  # exclude intercept
corrected_pvals_CTL08A = multipletests(pvals_CTL08A, method='fdr_bh')[1]

df_results_CTL08A = pd.concat([model_CTL08A.params, pd.Series(corrected_pvals_CTL08A, index=model_CTL08A.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL08A.columns = ['intercept', 'pvalue']
In [49]:
pvals_CTL04E = model_CTL04E.pvalues[1:]  # exclude intercept
corrected_pvals_CTL04E = multipletests(pvals_CTL04E, method='fdr_bh')[1]

df_results_CTL04E = pd.concat([model_CTL04E.params, pd.Series(corrected_pvals_CTL04E, index=model_CTL04E.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL04E.columns = ['intercept', 'pvalue']
In [50]:
pvals = pd.concat([df_results_CTL08A['pvalue'], df_results_CTL04E['pvalue']], axis = 1)
pvals.columns = ['CTL08A', 'CTL04E']
pvals.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in pvals.index]
pvals
Out[50]:
CTL08A CTL04E
ANDR_AG 0.503368 0.959467
ANDR_INH 0.003224 0.674231
ARYL_AG 0.048640 0.033257
ARYL_INH 0.007327 0.935059
ESTR_AG 0.004118 0.306303
ESTR_INH 0.264495 0.284239
GC_AG 0.203314 NaN
GC_INH 0.224817 0.216278
LX_AG 0.007327 0.332531
LX_INH 0.084566 0.122681
RET_AG 0.000680 NaN
RET_INH 0.339600 0.183886
THYR_AG 0.007327 0.288888
THYR_INH 0.674887 0.047296
In [51]:
intercepts = pd.concat([df_results_CTL08A['intercept'], df_results_CTL04E['intercept']], axis = 1)
intercepts.columns = ['CTL08A', 'CTL04E']
intercepts.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in intercepts.index]
intercepts = (np.exp(intercepts) - 1) * 100
intercepts
Out[51]:
CTL08A CTL04E
ANDR_AG -24.170411 1.902158
ANDR_INH -81.880094 26.198362
ARYL_AG -54.900314 188.387416
ARYL_INH -68.882331 -5.911741
ESTR_AG -77.089349 66.950283
ESTR_INH -41.627377 65.228707
GC_AG -47.670945 NaN
GC_INH -41.557494 78.932108
LX_AG -68.655867 -32.456484
LX_INH -57.312929 111.427435
RET_AG -80.687425 NaN
RET_INH -33.158847 90.499384
THYR_AG -68.917410 60.069424
THYR_INH -14.742162 -71.345140
In [52]:
fig, ax = plt.subplots(figsize = (4,7))
intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")
intercepts = intercepts[pvals < 0.05].fillna('')
sns.heatmap(pvals, annot=intercepts, fmt='', cmap='Greens_r', cbar_kws={'label': 'p-value'}, vmin=0, vmax=0.05, ax = ax)
plt.title('Condition vs DMSO\n')
plt.savefig('../../../../endpoints_figures/endpoints_organoids_area_OLS_test.png', dpi=300, bbox_inches='tight')
/tmp/ipykernel_359582/1523563738.py:2: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead.
  intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")

Compute density¶

In [53]:
n_nuclei_mix1 = pd.read_csv('../tables/n_nuclei_mix1.csv')
n_nuclei_mix2 = pd.read_csv('../tables/n_nuclei_mix2.csv')
n_nuclei = pd.concat([n_nuclei_mix1, n_nuclei_mix2])
In [54]:
n_nuclei.ImageName = n_nuclei.ImageName.str.replace('_channel_1', '')
In [55]:
n_nuclei.set_index('ImageName', inplace = True)
In [56]:
organoid_areas['n_nuclei'] = n_nuclei['count']
In [57]:
organoid_areas['density'] = organoid_areas['n_nuclei'] / organoid_areas['Area_um2']

Number of nuclei per condition and line¶

In [58]:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle

fig, axs = plt.subplots(1, 2, figsize=(15, 7))
bar_width = 0.6

for ax, line in zip(axs, ['CTL08A', 'CTL04E']):
    df = organoid_areas[organoid_areas['Line'] == line]
    means = df.groupby('Condition')['n_nuclei'].mean().reindex(hormonal_conditions)
    sems = df.groupby('Condition')['n_nuclei'].sem().reindex(hormonal_conditions)

    x = np.arange(len(hormonal_conditions))

    for i, cond in enumerate(hormonal_conditions):
        mean = means[cond]
        sem = sems[cond]

        # Add bar as a Rectangle
        bar = Rectangle(
            (i - bar_width / 2, 0),  # bottom-left x, y
            bar_width,
            mean,
            color=color_palette[cond],
            zorder=1
        )
        ax.add_patch(bar)

        # Add error bar
        ax.errorbar(
            i, mean, yerr=sem,
            fmt='none',
            ecolor='black',
            capsize=5,
            zorder=2
        )

        # Add strip dots
        y_vals = df[df['Condition'] == cond]['n_nuclei']
        jittered_x = i + np.random.uniform(-bar_width/3, bar_width/3, size=len(y_vals))
        ax.scatter(jittered_x, y_vals, color='black', alpha=0.3, s=10, linewidth=0.5, zorder=3)

    # Format
    ax.set_title(line)
    ax.set_xticks(x)
    ax.set_xticklabels(hormonal_conditions, rotation=90)
    ax.set_xlim(-0.5, len(hormonal_conditions) - 0.5)
    ax.set_ylabel("Total number of nuclei")

plt.tight_layout(rect=[0, 0.03, 1, 0.95])

plt.savefig('../../../../endpoints_figures/number_of_nuclei_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/number_of_nuclei_per_condition.png', dpi=300, bbox_inches='tight')
plt.show()
In [59]:
sns.scatterplot(x = organoid_areas['Area_um2'], y = organoid_areas['n_nuclei'], s=50, hue = organoid_areas['Condition'], palette=color_palette)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

plt.savefig('../../../../endpoints_figures/scatterplot_area_n_nuclei.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/scatterplot_area_n_nuclei.png', dpi=300, bbox_inches='tight')
In [60]:
sns.scatterplot(x = np.log10(organoid_areas['Area_um2']), y = np.log10(organoid_areas['n_nuclei']), s=50, hue = organoid_areas['Condition'], palette=color_palette)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')


plt.savefig('../../../../endpoints_figures/scatterplot_area_n_nuclei.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/scatterplot_area_n_nuclei.png', dpi=300, bbox_inches='tight')
In [61]:
sns.scatterplot(x = np.log10(organoid_areas['Area_um2']), y = np.log10(organoid_areas['n_nuclei']), s=50, hue = organoid_areas['Line'], palette=line_palette)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
Out[61]:
<matplotlib.legend.Legend at 0x15551b74ef50>

Statistical test - Number of nuclei¶

In [62]:
organoid_areas_CTL08A = organoid_areas[organoid_areas['Line'] == 'CTL08A']
organoid_areas_CTL08A['n_nuclei'].hist(bins = 10)
Out[62]:
<Axes: >
In [63]:
organoid_areas_CTL08A["log_n_nuclei"] = np.log(organoid_areas_CTL08A["n_nuclei"])
organoid_areas_CTL08A['log_n_nuclei'].hist(bins = 10)
/tmp/ipykernel_359582/1482013400.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL08A["log_n_nuclei"] = np.log(organoid_areas_CTL08A["n_nuclei"])
Out[63]:
<Axes: >
In [64]:
model_CTL08A = smf.ols("log_n_nuclei  ~ C(Condition, Treatment(reference='DMSO'))",
                data=organoid_areas_CTL08A,
                ).fit()

print(model_CTL08A.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:           log_n_nuclei   R-squared:                       0.522
Model:                            OLS   Adj. R-squared:                  0.422
Method:                 Least Squares   F-statistic:                     5.232
Date:                Thu, 05 Mar 2026   Prob (F-statistic):           1.52e-06
Time:                        11:17:42   Log-Likelihood:                -90.432
No. Observations:                  82   AIC:                             210.9
Df Residuals:                      67   BIC:                             247.0
Df Model:                          14                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                10.9638      0.329     33.301      0.000      10.307      11.621
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]     -0.4239      0.466     -0.910      0.366      -1.353       0.506
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]    -2.4438      0.570     -4.285      0.000      -3.582      -1.306
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]     -1.0388      0.425     -2.444      0.017      -1.887      -0.190
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]    -1.5445      0.466     -3.317      0.001      -2.474      -0.615
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]     -1.8597      0.521     -3.572      0.001      -2.899      -0.821
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]    -0.7584      0.521     -1.457      0.150      -1.797       0.281
C(Condition, Treatment(reference='DMSO'))[T.GC_AG]       -0.7253      0.521     -1.393      0.168      -1.764       0.314
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]      -0.6386      0.466     -1.371      0.175      -1.568       0.291
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]       -1.4937      0.466     -3.208      0.002      -2.423      -0.564
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]      -0.7846      0.521     -1.507      0.136      -1.824       0.254
C(Condition, Treatment(reference='DMSO'))[T.RET_AG]      -2.7544      0.466     -5.916      0.000      -3.684      -1.825
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]     -0.4159      0.466     -0.893      0.375      -1.345       0.513
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]     -1.1299      0.466     -2.427      0.018      -2.059      -0.201
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]    -0.0765      0.466     -0.164      0.870      -1.006       0.853
==============================================================================
Omnibus:                        6.800   Durbin-Watson:                   1.928
Prob(Omnibus):                  0.033   Jarque-Bera (JB):                8.262
Skew:                          -0.367   Prob(JB):                       0.0161
Kurtosis:                       4.371   Cond. No.                         15.3
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [65]:
organoid_areas_CTL04E = organoid_areas[organoid_areas['Line'] == 'CTL04E']
organoid_areas_CTL04E['Condition'] = organoid_areas_CTL04E['Condition'].astype('category')
organoid_areas_CTL04E["log_n_nuclei"] = np.log(organoid_areas_CTL04E["n_nuclei"])
organoid_areas_CTL04E['log_n_nuclei'].hist(bins = 10)

model_CTL04E = smf.ols("log_n_nuclei ~ C(Condition, Treatment(reference='DMSO'))",
                data=organoid_areas_CTL04E,
                ).fit()

print(model_CTL04E.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:           log_n_nuclei   R-squared:                       0.490
Model:                            OLS   Adj. R-squared:                  0.370
Method:                 Least Squares   F-statistic:                     4.083
Date:                Thu, 05 Mar 2026   Prob (F-statistic):           0.000189
Time:                        11:17:42   Log-Likelihood:                -62.617
No. Observations:                  64   AIC:                             151.2
Df Residuals:                      51   BIC:                             179.3
Df Model:                          12                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                10.2373      0.361     28.395      0.000       9.513      10.961
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]     -0.3964      0.510     -0.777      0.441      -1.420       0.627
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]    -0.1566      0.551     -0.284      0.777      -1.262       0.949
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]      0.8781      0.465      1.887      0.065      -0.056       1.812
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]    -0.1838      0.465     -0.395      0.695      -1.118       0.751
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]      0.1943      0.551      0.353      0.726      -0.911       1.300
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]     0.2342      0.465      0.503      0.617      -0.700       1.169
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]       0.6839      0.465      1.469      0.148      -0.250       1.618
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]       -1.1444      0.465     -2.459      0.017      -2.079      -0.210
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]       0.6637      0.465      1.426      0.160      -0.271       1.598
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]      0.4658      0.465      1.001      0.322      -0.469       1.400
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]      0.3717      0.465      0.799      0.428      -0.563       1.306
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]    -1.5616      0.624     -2.501      0.016      -2.815      -0.308
==============================================================================
Omnibus:                       17.531   Durbin-Watson:                   1.783
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               33.590
Skew:                          -0.863   Prob(JB):                     5.08e-08
Kurtosis:                       6.101   Cond. No.                         15.5
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
/tmp/ipykernel_359582/3888406708.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL04E['Condition'] = organoid_areas_CTL04E['Condition'].astype('category')
/tmp/ipykernel_359582/3888406708.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL04E["log_n_nuclei"] = np.log(organoid_areas_CTL04E["n_nuclei"])
In [66]:
resid_CTL08A = model_CTL08A.resid

stats.probplot(resid_CTL08A, dist="norm", plot=plt)
plt.show()
In [67]:
resid_CTL04E = model_CTL04E.resid

stats.probplot(resid_CTL04E, dist="norm", plot=plt)
plt.show()
In [68]:
pvals_CTL08A = model_CTL08A.pvalues[1:]  # exclude intercept
corrected_pvals_CTL08A = multipletests(pvals_CTL08A, method='fdr_bh')[1]

df_results_CTL08A = pd.concat([model_CTL08A.params, pd.Series(corrected_pvals_CTL08A, index=model_CTL08A.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL08A.columns = ['intercept', 'pvalue']
In [69]:
pvals_CTL04E = model_CTL04E.pvalues[1:]  # exclude intercept
corrected_pvals_CTL04E = multipletests(pvals_CTL04E, method='fdr_bh')[1]

df_results_CTL04E = pd.concat([model_CTL04E.params, pd.Series(corrected_pvals_CTL04E, index=model_CTL04E.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL04E.columns = ['intercept', 'pvalue']
In [70]:
pvals = pd.concat([df_results_CTL08A['pvalue'], df_results_CTL04E['pvalue']], axis = 1)
pvals.columns = ['CTL08A', 'CTL04E']
pvals.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in pvals.index]
pvals
Out[70]:
CTL08A CTL04E
ANDR_AG 0.403765 0.660794
ANDR_INH 0.000418 0.777295
ARYL_AG 0.035865 0.259707
ARYL_INH 0.005151 0.777295
ESTR_AG 0.003083 0.777295
ESTR_INH 0.222476 0.777295
GC_AG 0.222476 NaN
GC_INH 0.222476 0.383899
LX_AG 0.005744 0.104285
LX_INH 0.222476 0.383899
RET_AG 0.000002 NaN
RET_INH 0.403765 0.643285
THYR_AG 0.035865 0.660794
THYR_INH 0.869907 0.104285
In [71]:
intercepts = pd.concat([df_results_CTL08A['intercept'], df_results_CTL04E['intercept']], axis = 1)
intercepts.columns = ['CTL08A', 'CTL04E']
intercepts.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in intercepts.index]
intercepts = (np.exp(intercepts) - 1) * 100
In [72]:
fig, ax = plt.subplots(figsize = (4,7))
intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")
intercepts = intercepts[pvals < 0.05].fillna('')
sns.heatmap(pvals, annot=intercepts, fmt='', cmap='Greens_r', cbar_kws={'label': 'p-value'}, vmin=0, vmax=0.05, ax = ax)
plt.title('Condition vs DMSO\n')
plt.savefig('../../../../endpoints_figures/endpoints_organoids_n_nuclei_OLS_test.png', dpi=300, bbox_inches='tight')
/tmp/ipykernel_359582/1529930461.py:2: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead.
  intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")

Plot of density¶

In [73]:
fig, ax = plt.subplots(1, 2, figsize=(20, 5))
organoid_areas = organoid_areas[organoid_areas['Condition'].isin(hormonal_conditions)]
for i, cond in enumerate(hormonal_conditions):
    data = organoid_areas[(organoid_areas['Line'] == 'CTL08A') & 
                                 (organoid_areas['Condition'] == cond)]['density']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = organoid_areas[(organoid_areas['Condition'] == 'DMSO') & 
                                       (organoid_areas['Line'] == 'CTL08A')]['density'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=organoid_areas[(organoid_areas['Line'] == 'CTL08A')],
    y='density', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = organoid_areas[(organoid_areas['Line'] == 'CTL04E') & 
                                 (organoid_areas['Condition'] == cond)]['density']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = organoid_areas[(organoid_areas['Condition'] == 'DMSO') & 
                                       (organoid_areas['Line'] == 'CTL04E')]['density'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=organoid_areas[organoid_areas['Line'] == 'CTL04E'],
    y='density', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('Density (nuclei/um²)')
ax[1].set_ylabel('Density (nuclei/um²)')

plt.savefig('../../../../endpoints_figures/endpoints_organoids_density_grouped.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/endpoints_organoids_density_grouped.png', dpi=300, bbox_inches='tight')
In [74]:
density_dict = {i:j for i, j in zip(organoid_areas.index, organoid_areas['density'])}
area_dict = {i:j for i, j in zip(organoid_areas.index, organoid_areas['Area_um2'])}

Statistical test - Density¶

In [75]:
organoid_areas_CTL08A = organoid_areas[organoid_areas['Line'] == 'CTL08A']
organoid_areas_CTL08A['density'].hist(bins = 10)
Out[75]:
<Axes: >
In [76]:
organoid_areas_CTL08A["log_density"] = np.log(organoid_areas_CTL08A["density"])
organoid_areas_CTL08A['log_density'].hist(bins = 10)
/tmp/ipykernel_359582/3272618323.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL08A["log_density"] = np.log(organoid_areas_CTL08A["density"])
Out[76]:
<Axes: >
In [77]:
model_CTL08A = smf.ols("log_density ~ C(Condition, Treatment(reference='DMSO'))",
                data=organoid_areas_CTL08A,
                ).fit()

print(model_CTL08A.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:            log_density   R-squared:                       0.684
Model:                            OLS   Adj. R-squared:                  0.618
Method:                 Least Squares   F-statistic:                     10.37
Date:                Thu, 05 Mar 2026   Prob (F-statistic):           6.74e-12
Time:                        11:18:52   Log-Likelihood:                 11.210
No. Observations:                  82   AIC:                             7.580
Df Residuals:                      67   BIC:                             43.68
Df Model:                          14                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                -4.5218      0.095    -47.438      0.000      -4.712      -4.332
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]     -0.1472      0.135     -1.092      0.279      -0.416       0.122
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]    -0.7356      0.165     -4.455      0.000      -1.065      -0.406
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]     -0.2425      0.123     -1.971      0.053      -0.488       0.003
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]    -0.3771      0.135     -2.798      0.007      -0.646      -0.108
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]     -0.3862      0.151     -2.562      0.013      -0.687      -0.085
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]    -0.2201      0.151     -1.460      0.149      -0.521       0.081
C(Condition, Treatment(reference='DMSO'))[T.GC_AG]       -0.0776      0.151     -0.515      0.608      -0.378       0.223
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]      -0.1014      0.135     -0.753      0.454      -0.371       0.168
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]       -0.3335      0.135     -2.474      0.016      -0.603      -0.064
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]       0.0667      0.151      0.443      0.660      -0.234       0.368
C(Condition, Treatment(reference='DMSO'))[T.RET_AG]      -1.1100      0.135     -8.234      0.000      -1.379      -0.841
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]     -0.0131      0.135     -0.097      0.923      -0.282       0.256
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]      0.0386      0.135      0.286      0.775      -0.230       0.308
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]     0.0829      0.135      0.615      0.540      -0.186       0.352
==============================================================================
Omnibus:                        2.586   Durbin-Watson:                   1.869
Prob(Omnibus):                  0.274   Jarque-Bera (JB):                1.962
Skew:                           0.215   Prob(JB):                        0.375
Kurtosis:                       3.624   Cond. No.                         15.3
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [78]:
organoid_areas_CTL04E = organoid_areas[organoid_areas['Line'] == 'CTL04E']
organoid_areas_CTL04E['Condition'] = organoid_areas_CTL04E['Condition'].astype('category')
organoid_areas_CTL04E["log_density"] = np.log(organoid_areas_CTL04E["density"])
organoid_areas_CTL04E['log_density'].hist(bins = 10)

model_CTL04E = smf.ols("log_density ~ C(Condition, Treatment(reference='DMSO'))",
                data=organoid_areas_CTL04E,
                ).fit()

print(model_CTL04E.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:            log_density   R-squared:                       0.452
Model:                            OLS   Adj. R-squared:                  0.323
Method:                 Least Squares   F-statistic:                     3.505
Date:                Thu, 05 Mar 2026   Prob (F-statistic):           0.000817
Time:                        11:18:52   Log-Likelihood:               0.057152
No. Observations:                  64   AIC:                             25.89
Df Residuals:                      51   BIC:                             53.95
Df Model:                          12                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                -4.4799      0.135    -33.084      0.000      -4.752      -4.208
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]     -0.4152      0.191     -2.168      0.035      -0.800      -0.031
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]    -0.3893      0.207     -1.882      0.066      -0.805       0.026
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]     -0.1811      0.175     -1.036      0.305      -0.532       0.170
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]    -0.1229      0.175     -0.703      0.485      -0.474       0.228
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]     -0.3182      0.207     -1.538      0.130      -0.733       0.097
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]    -0.2680      0.175     -1.533      0.132      -0.619       0.083
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]       0.1021      0.175      0.584      0.562      -0.249       0.453
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]       -0.7520      0.175     -4.302      0.000      -1.103      -0.401
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]      -0.0850      0.175     -0.486      0.629      -0.436       0.266
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]     -0.1787      0.175     -1.022      0.312      -0.530       0.172
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]     -0.0987      0.175     -0.565      0.575      -0.450       0.252
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]    -0.3118      0.235     -1.329      0.190      -0.783       0.159
==============================================================================
Omnibus:                       33.100   Durbin-Watson:                   1.679
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              171.463
Skew:                          -1.206   Prob(JB):                     5.85e-38
Kurtosis:                      10.647   Cond. No.                         15.5
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
/tmp/ipykernel_359582/4082485772.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL04E['Condition'] = organoid_areas_CTL04E['Condition'].astype('category')
/tmp/ipykernel_359582/4082485772.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  organoid_areas_CTL04E["log_density"] = np.log(organoid_areas_CTL04E["density"])
In [79]:
resid_CTL08A = model_CTL08A.resid

stats.probplot(resid_CTL08A, dist="norm", plot=plt)
plt.show()
In [80]:
resid_CTL04E = model_CTL04E.resid

stats.probplot(resid_CTL04E, dist="norm", plot=plt)
plt.show()
In [81]:
pvals_CTL08A = model_CTL08A.pvalues[1:]  # exclude intercept
corrected_pvals_CTL08A = multipletests(pvals_CTL08A, method='fdr_bh')[1]

df_results_CTL08A = pd.concat([model_CTL08A.params, pd.Series(corrected_pvals_CTL08A, index=model_CTL08A.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL08A.columns = ['intercept', 'pvalue']
In [82]:
pvals_CTL04E = model_CTL04E.pvalues[1:]  # exclude intercept
corrected_pvals_CTL04E = multipletests(pvals_CTL04E, method='fdr_bh')[1]

df_results_CTL04E = pd.concat([model_CTL04E.params, pd.Series(corrected_pvals_CTL04E, index=model_CTL04E.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL04E.columns = ['intercept', 'pvalue']
In [83]:
pvals = pd.concat([df_results_CTL08A['pvalue'], df_results_CTL04E['pvalue']], axis = 1)
pvals.columns = ['CTL08A', 'CTL04E']
pvals.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in pvals.index]
pvals
Out[83]:
CTL08A CTL04E
ANDR_AG 4.879720e-01 0.209006
ANDR_INH 2.281347e-04 0.262176
ARYL_AG 1.234503e-01 0.467429
ARYL_INH 3.132806e-02 0.626950
ESTR_AG 4.429340e-02 0.315606
ESTR_INH 2.977829e-01 0.315606
GC_AG 7.694695e-01 NaN
GC_INH 7.067643e-01 0.626950
LX_AG 4.450044e-02 0.000921
LX_INH 7.694695e-01 0.628969
RET_AG 1.276243e-10 NaN
RET_INH 9.231513e-01 0.467429
THYR_AG 8.350771e-01 0.626950
THYR_INH 7.566438e-01 0.379241
In [87]:
intercepts = pd.concat([df_results_CTL08A['intercept'], df_results_CTL04E['intercept']], axis = 1)
intercepts.columns = ['CTL08A', 'CTL04E']
intercepts.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in intercepts.index]
intercepts = (np.exp(intercepts) - 1) * 100
intercepts
Out[87]:
CTL08A CTL04E
ANDR_AG -13.685767 -33.979428
ANDR_INH -52.078278 -32.245714
ARYL_AG -21.533218 -16.562041
ARYL_INH -31.418007 -11.563816
ESTR_AG -32.034081 -27.254041
ESTR_INH -19.754827 -23.505846
GC_AG -7.471028 NaN
GC_INH -9.647208 10.750290
LX_AG -28.360952 -52.856458
LX_INH 6.896722 -8.146840
RET_AG -67.042535 NaN
RET_INH -1.296808 -16.360442
THYR_AG 3.936766 -9.401252
THYR_INH 8.647752 -26.787315
In [88]:
fig, ax = plt.subplots(figsize = (4,7))
intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")
intercepts = intercepts[pvals < 0.05].fillna('')
sns.heatmap(pvals, annot=intercepts, cmap='Greens_r', cbar_kws={'label': 'p-value'}, vmin=0, vmax=0.05, ax = ax, fmt='')
plt.title('Condition vs DMSO\n')
plt.savefig('../../../../endpoints_figures/endpoints_organoids_density_OLS_test.png', dpi=300, bbox_inches='tight')
/tmp/ipykernel_359582/2873717824.py:2: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead.
  intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")

MIX 1¶

Channel 2 - SOX2¶

In [548]:
tot_df_mix1_ch2 = pd.DataFrame()
measurements_files_mix_1_ch2 = [f for f in measurements_files_mix_1 if 'channel_2' in f]

for file in measurements_files_mix_1_ch2:
    print(file)
    df = pd.read_csv(file, index_col=0)
    df['ImageName'] = file.split("/")[-1].split(".")[0]
    print(df.shape)
    tot_df_mix1_ch2 = pd.concat([tot_df_mix1_ch2, df], axis=0)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion3_rep_1_channel_2_measures.csv
(14032, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion2_rep_0_channel_2_measures.csv
(36257, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion3_rep_1_channel_2_measures.csv
(29165, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion2_rep_0_channel_2_measures.csv
(28234, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion1_rep_2_channel_2_measures.csv
(5186, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion2_rep_1_channel_2_measures.csv
(30598, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion0_rep_0_channel_2_measures.csv
(4420, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion4_rep_0_channel_2_measures.csv
(5637, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion5_rep_2_channel_2_measures.csv
(44878, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion2_rep_1_channel_2_measures.csv
(25453, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion5_rep_0_channel_2_measures.csv
(14547, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion2_rep_0_channel_2_measures.csv
(65200, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion4_rep_1_channel_2_measures.csv
(18423, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion0_rep_2_channel_2_measures.csv
(12038, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion0_rep_1_channel_2_measures.csv
(52451, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion0_rep_0_channel_2_measures.csv
(69681, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion3_rep_0_channel_2_measures.csv
(60277, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion7_rep_0_channel_2_measures.csv
(36423, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion2_rep_1_channel_2_measures.csv
(54724, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion3_rep_2_channel_2_measures.csv
(48396, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion1_rep_0_channel_2_measures.csv
(18479, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion5_rep_0_channel_2_measures.csv
(61473, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion3_rep_2_channel_2_measures.csv
(62393, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion5_rep_2_channel_2_measures.csv
(27998, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion4_rep_0_channel_2_measures.csv
(16418, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion0_rep_0_channel_2_measures.csv
(54097, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion0_rep_1_channel_2_measures.csv
(19128, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion2_rep_2_channel_2_measures.csv
(7446, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion0_rep_0_channel_2_measures.csv
(6943, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion3_rep_2_channel_2_measures.csv
(44660, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion2_rep_0_channel_2_measures.csv
(6134, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion2_rep_0_channel_2_measures.csv
(43655, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion5_rep_0_channel_2_measures.csv
(55824, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion4_rep_0_channel_2_measures.csv
(43161, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion4_rep_2_channel_2_measures.csv
(26186, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion5_rep_2_channel_2_measures.csv
(42766, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion1_rep_0_channel_2_measures.csv
(32479, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion0_rep_2_channel_2_measures.csv
(46684, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion1_rep_1_channel_2_measures.csv
(7889, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion7_rep_1_channel_2_measures.csv
(1, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion3_rep_1_channel_2_measures.csv
(4878, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion5_rep_1_channel_2_measures.csv
(24682, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion3_rep_1_channel_2_measures.csv
(31993, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion3_rep_2_channel_2_measures.csv
(48939, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion4_rep_2_channel_2_measures.csv
(1405, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion0_rep_2_channel_2_measures.csv
(50003, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion4_rep_1_channel_2_measures.csv
(26358, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion1_rep_1_channel_2_measures.csv
(4685, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion0_rep_1_channel_2_measures.csv
(42864, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion4_rep_1_channel_2_measures.csv
(12551, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion1_rep_0_channel_2_measures.csv
(128250, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion2_rep_2_channel_2_measures.csv
(68825, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion0_rep_1_channel_2_measures.csv
(10615, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion2_rep_2_channel_2_measures.csv
(54944, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion2_rep_1_channel_2_measures.csv
(32605, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion2_rep_0_channel_2_measures.csv
(23699, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion3_rep_1_channel_2_measures.csv
(20919, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion4_rep_2_channel_2_measures.csv
(33055, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion3_rep_0_channel_2_measures.csv
(70503, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion1_rep_0_channel_2_measures.csv
(12349, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion1_rep_1_channel_2_measures.csv
(30782, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion6_rep_0_channel_2_measures.csv
(22787, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion4_rep_0_channel_2_measures.csv
(42538, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion5_rep_1_channel_2_measures.csv
(31370, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion3_rep_0_channel_2_measures.csv
(4465, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion0_rep_1_channel_2_measures.csv
(24746, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion1_rep_1_channel_2_measures.csv
(5061, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion5_rep_2_channel_2_measures.csv
(45127, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion1_rep_1_channel_2_measures.csv
(12207, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion5_rep_0_channel_2_measures.csv
(36668, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion4_rep_0_channel_2_measures.csv
(22478, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion0_rep_0_channel_2_measures.csv
(75915, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion4_rep_0_channel_2_measures.csv
(881, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion1_rep_2_channel_2_measures.csv
(25173, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion0_rep_1_channel_2_measures.csv
(9836, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion7_rep_2_channel_2_measures.csv
(27770, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion5_rep_0_channel_2_measures.csv
(52956, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion2_rep_2_channel_2_measures.csv
(55563, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion1_rep_1_channel_2_measures.csv
(92340, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion2_rep_1_channel_2_measures.csv
(2185, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion2_rep_2_channel_2_measures.csv
(13307, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion3_rep_0_channel_2_measures.csv
(47582, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion0_rep_2_channel_2_measures.csv
(1122, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion1_rep_0_channel_2_measures.csv
(5646, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion6_rep_1_channel_2_measures.csv
(22272, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion5_rep_1_channel_2_measures.csv
(59825, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion3_rep_0_channel_2_measures.csv
(11258, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion4_rep_2_channel_2_measures.csv
(6712, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion1_rep_0_channel_2_measures.csv
(26007, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion7_rep_3_channel_2_measures.csv
(72679, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion2_rep_1_channel_2_measures.csv
(17407, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion6_rep_2_channel_2_measures.csv
(4246, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion5_rep_1_channel_2_measures.csv
(79266, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion4_rep_1_channel_2_measures.csv
(10935, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion2_rep_0_channel_2_measures.csv
(24890, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion0_rep_1_channel_2_measures.csv
(24601, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion5_rep_2_channel_2_measures.csv
(58504, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion2_rep_2_channel_2_measures.csv
(7832, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion2_rep_1_channel_2_measures.csv
(194410, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion1_rep_0_channel_2_measures.csv
(2649, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion0_rep_2_channel_2_measures.csv
(35739, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion5_rep_1_channel_2_measures.csv
(72165, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion4_rep_0_channel_2_measures.csv
(79123, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion4_rep_1_channel_2_measures.csv
(17120, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion3_rep_0_channel_2_measures.csv
(43389, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion3_rep_2_channel_2_measures.csv
(19504, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0131_ScanRegion4_rep_1_channel_2_measures.csv
(38346, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0128_ScanRegion0_rep_0_channel_2_measures.csv
(70479, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0134_ScanRegion3_rep_0_channel_2_measures.csv
(50144, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion3_rep_2_channel_2_measures.csv
(1195, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion3_rep_1_channel_2_measures.csv
(54110, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0133_ScanRegion4_rep_1_channel_2_measures.csv
(4832, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0132_ScanRegion3_rep_1_channel_2_measures.csv
(39112, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0130_ScanRegion0_rep_0_channel_2_measures.csv
(41176, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion3_rep_3_channel_2_measures.csv
(9818, 19)
/group/testa/Project/EndPoints/TPSSU/analysis/measurements/20240424_manuel_lessi_0129_ScanRegion3_rep_4_channel_2_measures.csv
(23465, 19)
In [549]:
#tot_df_mix1_ch2.to_csv("/group/testa/Project/EndPoints/TPSSU/analysis/measurements_mix_1_ch2_SOX2.csv")

Thresholding¶

In [550]:
tot_pos_SOX2 = {}
density_SOX2 = {}
n_nuclei = {}
n_nuclei_tot_pos_SOX2 = {}

for file in measurements_files_mix_1_ch2:
    #print(file)
    df = pd.read_csv(file, index_col=0)
    
    imageName_scanRegion_rep = file.split('/')[-1].split('.')[0].replace('_channel_2_measures', '')
    imageName_scanRegion = '_'.join(imageName_scanRegion_rep.split('_')[:5])
    
    if imageName_scanRegion_rep in good_tissue_to_keep_limit and imageName_scanRegion in sample_sheet['ImageName_ScanRegion'].values:
        area = area_dict[imageName_scanRegion_rep]
        
        df['ImageName'] = file.split("/")[-1].split(".")[0]
        tot_pos_SOX2[file.split("/")[-1].split(".")[0]] = {}
        density_SOX2[imageName_scanRegion_rep] = {}
        n_nuclei_tot_pos_SOX2[imageName_scanRegion_rep] = {}

        min_quantile = np.quantile(df['area'], 0.01)

        max_quantile = np.quantile(df['area'], 0.99)

        df = df[(df['area'] > min_quantile) & (df['area'] < max_quantile)]

        n_nuclei[file.split("/")[-1].split(".")[0]] = df.shape[0]

        otsu_thrs = filters.threshold_multiotsu(np.array(df['intensity_mean']), classes = 4)

        for i, thr in enumerate(otsu_thrs):
            tot_pos_SOX2[file.split("/")[-1].split(".")[0]][f'otsu_{i}'] = np.sum(df['intensity_mean'] > thr) / df.shape[0] * 100
            n_nuclei_tot_pos_SOX2[imageName_scanRegion_rep][f'otsu_{i}'] = np.sum(df['intensity_mean'] > thr)
            density_SOX2[imageName_scanRegion_rep][f'otsu_{i}'] = np.sum(df['intensity_mean'] > thr) / area * 100
In [551]:
df_percentages = pd.DataFrame.from_dict(tot_pos_SOX2, orient='index')
df_percentages['ImageName_ScanRegion'] = df_percentages.reset_index()['index'].apply(lambda x: '_'.join(x.split('_')[0:5])).values
df_percentages['ImageName_ScanRegion_rep'] = df_percentages.reset_index()['index'].apply(lambda x: '_'.join(x.split('_')[0:7])).values
df_percentages['Condition_Line'] = df_percentages['ImageName_ScanRegion'].map(sample_dict)
df_percentages = df_percentages[~df_percentages.Condition_Line.isna()]
df_percentages['Line'] = df_percentages['Condition_Line'].apply(lambda x: x.split('_')[-1])
df_percentages['Condition'] = df_percentages['Condition_Line'].str.replace('_CTL08A', '', regex=True)
df_percentages['Condition'] = df_percentages['Condition'].str.replace('_CTL04E', '', regex=True)

#df_percentages = pd.read_csv("/group/testa/Project/EndPoints/TPSSU/analysis/percentages_mix_1_ch2_SOX2_percentages.csv")
In [552]:
sox2_pos_cells = pd.DataFrame.from_dict(n_nuclei_tot_pos_SOX2, orient='index')
sox2_pos_cells['ImageName_ScanRegion'] = sox2_pos_cells.reset_index()['index'].apply(lambda x: '_'.join(x.split('_')[0:5])).values
sox2_pos_cells['ImageName_ScanRegion_rep'] = sox2_pos_cells.reset_index()['index'].apply(lambda x: '_'.join(x.split('_')[0:7])).values
sox2_pos_cells['Condition_Line'] = sox2_pos_cells['ImageName_ScanRegion'].map(sample_dict)
sox2_pos_cells = sox2_pos_cells[~sox2_pos_cells.Condition_Line.isna()]
sox2_pos_cells['Line'] = sox2_pos_cells['Condition_Line'].apply(lambda x: x.split('_')[-1])
sox2_pos_cells['Condition'] = sox2_pos_cells['Condition_Line'].str.replace('_CTL08A', '', regex=True)
sox2_pos_cells['Condition'] = sox2_pos_cells['Condition'].str.replace('_CTL04E', '', regex=True)
sox2_pos_cells
Out[552]:
otsu_0 otsu_1 otsu_2 ImageName_ScanRegion ImageName_ScanRegion_rep Condition_Line Line Condition
20240424_manuel_lessi_0129_ScanRegion3_rep_1 2907 1209 365 20240424_manuel_lessi_0129_ScanRegion3 20240424_manuel_lessi_0129_ScanRegion3_rep_1 ARYL_AG_CTL08A CTL08A ARYL_AG
20240424_manuel_lessi_0134_ScanRegion2_rep_0 9181 3059 783 20240424_manuel_lessi_0134_ScanRegion2 20240424_manuel_lessi_0134_ScanRegion2_rep_0 RET_INH_CTL08A CTL08A RET_INH
20240424_manuel_lessi_0129_ScanRegion1_rep_2 301 144 36 20240424_manuel_lessi_0129_ScanRegion1 20240424_manuel_lessi_0129_ScanRegion1_rep_2 ARYL_INH_CTL08A CTL08A ARYL_INH
20240424_manuel_lessi_0134_ScanRegion2_rep_1 9830 3475 872 20240424_manuel_lessi_0134_ScanRegion2 20240424_manuel_lessi_0134_ScanRegion2_rep_1 RET_INH_CTL08A CTL08A RET_INH
20240424_manuel_lessi_0133_ScanRegion4_rep_0 3333 1923 522 20240424_manuel_lessi_0133_ScanRegion4 20240424_manuel_lessi_0133_ScanRegion4_rep_0 RET_AG_CTL08A CTL08A RET_AG
... ... ... ... ... ... ... ... ...
20240424_manuel_lessi_0130_ScanRegion3_rep_1 22897 12283 4331 20240424_manuel_lessi_0130_ScanRegion3 20240424_manuel_lessi_0130_ScanRegion3_rep_1 LX_INH_CTL04E CTL04E LX_INH
20240424_manuel_lessi_0133_ScanRegion4_rep_1 1685 884 260 20240424_manuel_lessi_0133_ScanRegion4 20240424_manuel_lessi_0133_ScanRegion4_rep_1 RET_AG_CTL08A CTL08A RET_AG
20240424_manuel_lessi_0130_ScanRegion0_rep_0 10199 5549 1924 20240424_manuel_lessi_0130_ScanRegion0 20240424_manuel_lessi_0130_ScanRegion0_rep_0 LX_AG_CTL04E CTL04E LX_AG
20240424_manuel_lessi_0129_ScanRegion3_rep_3 3898 2172 762 20240424_manuel_lessi_0129_ScanRegion3 20240424_manuel_lessi_0129_ScanRegion3_rep_3 ARYL_AG_CTL08A CTL08A ARYL_AG
20240424_manuel_lessi_0129_ScanRegion3_rep_4 4919 2353 819 20240424_manuel_lessi_0129_ScanRegion3 20240424_manuel_lessi_0129_ScanRegion3_rep_4 ARYL_AG_CTL08A CTL08A ARYL_AG

65 rows × 8 columns

In [553]:
density_SOX2 = pd.DataFrame.from_dict(density_SOX2, orient='index').reset_index()
density_SOX2['ImageName_ScanRegion'] = density_SOX2['index'].apply(lambda x: '_'.join(x.split('_')[0:5]))
density_SOX2['ImageName_ScanRegion_rep'] = density_SOX2['index'].apply(lambda x: '_'.join(x.split('_')[0:7]))
density_SOX2['Condition_Line'] = density_SOX2['ImageName_ScanRegion'].map(sample_dict)
density_SOX2 = density_SOX2[~density_SOX2.Condition_Line.isna()]
density_SOX2['Line'] = density_SOX2['Condition_Line'].apply(lambda x: x.split('_')[-1])
density_SOX2['Condition'] = density_SOX2['Condition_Line'].str.replace('_CTL08A', '', regex=True)
density_SOX2['Condition'] = density_SOX2['Condition'].str.replace('_CTL04E', '', regex=True)

print(len(density_SOX2))
#density_SOX2.to_csv("/group/testa/Project/EndPoints/TPSSU/analysis/SOX2_pos_over_area_mix_1_ch2.csv")
65
In [554]:
n_nuclei = pd.DataFrame.from_dict(n_nuclei, orient='index').reset_index()
n_nuclei['ImageName_ScanRegion'] = n_nuclei['index'].apply(lambda x: '_'.join(x.split('_')[0:5]))
n_nuclei['ImageName_ScanRegion_rep'] = n_nuclei['index'].apply(lambda x: '_'.join(x.split('_')[0:7]))
n_nuclei['Condition_Line'] = n_nuclei['ImageName_ScanRegion'].map(sample_dict)
n_nuclei = n_nuclei[~n_nuclei.Condition_Line.isna()]
n_nuclei['Line'] = n_nuclei['Condition_Line'].apply(lambda x: x.split('_')[-1])
n_nuclei['Condition'] = n_nuclei['Condition_Line'].str.replace('_CTL08A', '', regex=True)
n_nuclei['Condition'] = n_nuclei['Condition'].str.replace('_CTL04E', '', regex=True)

Plots¶

Percentages¶

In [555]:
mix1_img_names = tot_df_mix1_ch2['ImageName'].apply(lambda x: '_'.join(x.split('_')[0:4])).value_counts().index.tolist()
In [556]:
check_qc_ch2['ImageName_ScanRegion'] = check_qc_ch2['Reference Image Report'].apply(lambda x: '_'.join(x.split('_')[0:5]))
check_qc_ch2['ImageName_ScanRegion_rep'] = check_qc_ch2['Reference Image Report'].apply(lambda x: '_'.join(x.split('_')[0:7]))
check_qc_ch2['ImageName'] = check_qc_ch2['Reference Image Report'].apply(lambda x: '_'.join(x.split('_')[0:4]))
check_qc_ch2_mix1 = check_qc_ch2[check_qc_ch2['ImageName'].isin(mix1_img_names)]
In [557]:
check_qc_ch2_mix1['general QC (keep or not)'].value_counts()
Out[557]:
general QC (keep or not)
YES      86
NO       15
LIMIT     6
Name: count, dtype: int64
In [558]:
check_qc_ch2_mix1
Out[558]:
Reference Image Report general QC (keep or not) ImageName_ScanRegion_Rep ImageName_ScanRegion ImageName_ScanRegion_rep ImageName
0 20240424_manuel_lessi_0128_ScanRegion0_rep_0_c... YES 20240424_manuel_lessi_0128_ScanRegion0_rep_0 20240424_manuel_lessi_0128_ScanRegion0 20240424_manuel_lessi_0128_ScanRegion0_rep_0 20240424_manuel_lessi_0128
1 20240424_manuel_lessi_0128_ScanRegion0_rep_1_c... YES 20240424_manuel_lessi_0128_ScanRegion0_rep_1 20240424_manuel_lessi_0128_ScanRegion0 20240424_manuel_lessi_0128_ScanRegion0_rep_1 20240424_manuel_lessi_0128
2 20240424_manuel_lessi_0128_ScanRegion1_rep_0_c... YES 20240424_manuel_lessi_0128_ScanRegion1_rep_0 20240424_manuel_lessi_0128_ScanRegion1 20240424_manuel_lessi_0128_ScanRegion1_rep_0 20240424_manuel_lessi_0128
3 20240424_manuel_lessi_0128_ScanRegion1_rep_1_c... YES 20240424_manuel_lessi_0128_ScanRegion1_rep_1 20240424_manuel_lessi_0128_ScanRegion1 20240424_manuel_lessi_0128_ScanRegion1_rep_1 20240424_manuel_lessi_0128
4 20240424_manuel_lessi_0128_ScanRegion2_rep_0_c... YES 20240424_manuel_lessi_0128_ScanRegion2_rep_0 20240424_manuel_lessi_0128_ScanRegion2 20240424_manuel_lessi_0128_ScanRegion2_rep_0 20240424_manuel_lessi_0128
... ... ... ... ... ... ...
102 20240424_manuel_lessi_0134_ScanRegion4_rep_2_c... NO 20240424_manuel_lessi_0134_ScanRegion4_rep_2 20240424_manuel_lessi_0134_ScanRegion4 20240424_manuel_lessi_0134_ScanRegion4_rep_2 20240424_manuel_lessi_0134
103 20240424_manuel_lessi_0134_ScanRegion5_rep_0_c... NO 20240424_manuel_lessi_0134_ScanRegion5_rep_0 20240424_manuel_lessi_0134_ScanRegion5 20240424_manuel_lessi_0134_ScanRegion5_rep_0 20240424_manuel_lessi_0134
104 20240424_manuel_lessi_0134_ScanRegion6_rep_0_c... YES 20240424_manuel_lessi_0134_ScanRegion6_rep_0 20240424_manuel_lessi_0134_ScanRegion6 20240424_manuel_lessi_0134_ScanRegion6_rep_0 20240424_manuel_lessi_0134
105 20240424_manuel_lessi_0134_ScanRegion6_rep_2_c... NO 20240424_manuel_lessi_0134_ScanRegion6_rep_2 20240424_manuel_lessi_0134_ScanRegion6 20240424_manuel_lessi_0134_ScanRegion6_rep_2 20240424_manuel_lessi_0134
106 20240424_manuel_lessi_0134_ScanRegion7_rep_3_c... YES 20240424_manuel_lessi_0134_ScanRegion7_rep_3 20240424_manuel_lessi_0134_ScanRegion7 20240424_manuel_lessi_0134_ScanRegion7_rep_3 20240424_manuel_lessi_0134

107 rows × 6 columns

In [559]:
to_remove = check_qc_ch2_mix1[check_qc_ch2_mix1['general QC (keep or not)'] == 'NO']['ImageName_ScanRegion_rep'].tolist()
len(to_remove)
Out[559]:
15
In [560]:
df_percentages = df_percentages[~df_percentages['ImageName_ScanRegion_rep'].isin(to_remove)]
len(df_percentages)
Out[560]:
65
In [561]:
sox2_pos_cells = sox2_pos_cells[~sox2_pos_cells['ImageName_ScanRegion_rep'].isin(to_remove)]
len(sox2_pos_cells)
Out[561]:
65
In [562]:
n_nuclei = n_nuclei[~n_nuclei['ImageName_ScanRegion_rep'].isin(to_remove)]
len(n_nuclei)
Out[562]:
65
In [563]:
order = df_percentages['Condition'].tolist()
order = list(dict.fromkeys(order))  # Remove duplicates while preserving order
order.remove('DMSO')
order.sort()
order = ['DMSO'] + order
In [564]:
fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df_percentages[(df_percentages['Line'] == 'CTL08A') & 
                                 (df_percentages['Condition'] == cond)]['otsu_1']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df_percentages[(df_percentages['Condition'] == 'DMSO') & 
                                       (df_percentages['Line'] == 'CTL08A')]['otsu_1'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df_percentages[(df_percentages['Line'] == 'CTL08A')],
    y='otsu_1', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df_percentages[(df_percentages['Line'] == 'CTL04E') & 
                                 (df_percentages['Condition'] == cond)]['otsu_1']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df_percentages[(df_percentages['Condition'] == 'DMSO') & 
                                       (df_percentages['Line'] == 'CTL04E')]['otsu_1'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df_percentages[df_percentages['Line'] == 'CTL04E'],
    y='otsu_1', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('SOX2+ nuclei (%)')
ax[1].set_ylabel('SOX2+ nuclei (%)')

plt.savefig('../../../../endpoints_figures/SOX2_pos_otsu_1_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/SOX2_pos_otsu_1_per_condition.png', dpi=300, bbox_inches='tight')
In [565]:
df_percentages[df_percentages.Condition.isin(['RET_AG', 'DMSO'])][['otsu_1', 'Condition']].sort_values(by='Condition', ascending=False)
Out[565]:
otsu_1 Condition
20240424_manuel_lessi_0133_ScanRegion4_rep_0_channel_2_measures 34.818034 RET_AG
20240424_manuel_lessi_0133_ScanRegion4_rep_2_channel_2_measures 37.808219 RET_AG
20240424_manuel_lessi_0133_ScanRegion4_rep_1_channel_2_measures 18.673426 RET_AG
20240424_manuel_lessi_0132_ScanRegion5_rep_0_channel_2_measures 11.246140 DMSO
20240424_manuel_lessi_0132_ScanRegion5_rep_2_channel_2_measures 12.400583 DMSO
20240424_manuel_lessi_0132_ScanRegion4_rep_1_channel_2_measures 26.132437 DMSO
20240424_manuel_lessi_0132_ScanRegion5_rep_1_channel_2_measures 7.145889 DMSO
20240424_manuel_lessi_0132_ScanRegion4_rep_0_channel_2_measures 11.837556 DMSO
In [566]:
sox2_pos_cells[sox2_pos_cells.Condition.isin(['RET_AG', 'DMSO'])][['otsu_1', 'Condition']].sort_values(by='Condition', ascending=False)
Out[566]:
otsu_1 Condition
20240424_manuel_lessi_0133_ScanRegion4_rep_0 1923 RET_AG
20240424_manuel_lessi_0133_ScanRegion4_rep_2 2484 RET_AG
20240424_manuel_lessi_0133_ScanRegion4_rep_1 884 RET_AG
20240424_manuel_lessi_0132_ScanRegion5_rep_0 6774 DMSO
20240424_manuel_lessi_0132_ScanRegion5_rep_2 5192 DMSO
20240424_manuel_lessi_0132_ScanRegion4_rep_1 2798 DMSO
20240424_manuel_lessi_0132_ScanRegion5_rep_1 5051 DMSO
20240424_manuel_lessi_0132_ScanRegion4_rep_0 9176 DMSO
In [567]:
df_percentages[df_percentages.Condition.isin(['RET_AG', 'DMSO'])][['otsu_1', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[567]:
otsu_1
Condition
DMSO 11.837556
RET_AG 34.818034
In [568]:
sox2_pos_cells[sox2_pos_cells.Condition.isin(['RET_AG', 'DMSO'])][['otsu_1', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[568]:
otsu_1
Condition
DMSO 5192.0
RET_AG 1923.0
In [569]:
sox2_pos_cells
Out[569]:
otsu_0 otsu_1 otsu_2 ImageName_ScanRegion ImageName_ScanRegion_rep Condition_Line Line Condition
20240424_manuel_lessi_0129_ScanRegion3_rep_1 2907 1209 365 20240424_manuel_lessi_0129_ScanRegion3 20240424_manuel_lessi_0129_ScanRegion3_rep_1 ARYL_AG_CTL08A CTL08A ARYL_AG
20240424_manuel_lessi_0134_ScanRegion2_rep_0 9181 3059 783 20240424_manuel_lessi_0134_ScanRegion2 20240424_manuel_lessi_0134_ScanRegion2_rep_0 RET_INH_CTL08A CTL08A RET_INH
20240424_manuel_lessi_0129_ScanRegion1_rep_2 301 144 36 20240424_manuel_lessi_0129_ScanRegion1 20240424_manuel_lessi_0129_ScanRegion1_rep_2 ARYL_INH_CTL08A CTL08A ARYL_INH
20240424_manuel_lessi_0134_ScanRegion2_rep_1 9830 3475 872 20240424_manuel_lessi_0134_ScanRegion2 20240424_manuel_lessi_0134_ScanRegion2_rep_1 RET_INH_CTL08A CTL08A RET_INH
20240424_manuel_lessi_0133_ScanRegion4_rep_0 3333 1923 522 20240424_manuel_lessi_0133_ScanRegion4 20240424_manuel_lessi_0133_ScanRegion4_rep_0 RET_AG_CTL08A CTL08A RET_AG
... ... ... ... ... ... ... ... ...
20240424_manuel_lessi_0130_ScanRegion3_rep_1 22897 12283 4331 20240424_manuel_lessi_0130_ScanRegion3 20240424_manuel_lessi_0130_ScanRegion3_rep_1 LX_INH_CTL04E CTL04E LX_INH
20240424_manuel_lessi_0133_ScanRegion4_rep_1 1685 884 260 20240424_manuel_lessi_0133_ScanRegion4 20240424_manuel_lessi_0133_ScanRegion4_rep_1 RET_AG_CTL08A CTL08A RET_AG
20240424_manuel_lessi_0130_ScanRegion0_rep_0 10199 5549 1924 20240424_manuel_lessi_0130_ScanRegion0 20240424_manuel_lessi_0130_ScanRegion0_rep_0 LX_AG_CTL04E CTL04E LX_AG
20240424_manuel_lessi_0129_ScanRegion3_rep_3 3898 2172 762 20240424_manuel_lessi_0129_ScanRegion3 20240424_manuel_lessi_0129_ScanRegion3_rep_3 ARYL_AG_CTL08A CTL08A ARYL_AG
20240424_manuel_lessi_0129_ScanRegion3_rep_4 4919 2353 819 20240424_manuel_lessi_0129_ScanRegion3 20240424_manuel_lessi_0129_ScanRegion3_rep_4 ARYL_AG_CTL08A CTL08A ARYL_AG

65 rows × 8 columns

In [570]:
check_area_df = organoid_areas.loc[sox2_pos_cells.ImageName_ScanRegion_rep.tolist()]
check_area_df
Out[570]:
area ImageName_ScanRegion Condition_Line Line Condition Area_um2 n_nuclei density
20240424_manuel_lessi_0129_ScanRegion3_rep_1 18992894 20240424_manuel_lessi_0129_ScanRegion3 ARYL_AG_CTL08A CTL08A ARYL_AG 2.006124e+06 14032 0.006995
20240424_manuel_lessi_0134_ScanRegion2_rep_0 35827100 20240424_manuel_lessi_0134_ScanRegion2 RET_INH_CTL08A CTL08A RET_INH 3.784237e+06 36257 0.009581
20240424_manuel_lessi_0129_ScanRegion1_rep_2 9802711 20240424_manuel_lessi_0129_ScanRegion1 ARYL_INH_CTL08A CTL08A ARYL_INH 1.035411e+06 5186 0.005009
20240424_manuel_lessi_0134_ScanRegion2_rep_1 30831525 20240424_manuel_lessi_0134_ScanRegion2 RET_INH_CTL08A CTL08A RET_INH 3.256580e+06 30598 0.009396
20240424_manuel_lessi_0133_ScanRegion4_rep_0 8422522 20240424_manuel_lessi_0133_ScanRegion4 RET_AG_CTL08A CTL08A RET_AG 8.896289e+05 5637 0.006336
... ... ... ... ... ... ... ... ...
20240424_manuel_lessi_0130_ScanRegion3_rep_1 53795625 20240424_manuel_lessi_0130_ScanRegion3 LX_INH_CTL04E CTL04E LX_INH 5.682163e+06 54110 0.009523
20240424_manuel_lessi_0133_ScanRegion4_rep_1 11112634 20240424_manuel_lessi_0133_ScanRegion4 RET_AG_CTL08A CTL08A RET_AG 1.173772e+06 4832 0.004117
20240424_manuel_lessi_0130_ScanRegion0_rep_0 38195586 20240424_manuel_lessi_0130_ScanRegion0 LX_AG_CTL04E CTL04E LX_AG 4.034409e+06 41176 0.010206
20240424_manuel_lessi_0129_ScanRegion3_rep_3 12134498 20240424_manuel_lessi_0129_ScanRegion3 ARYL_AG_CTL08A CTL08A ARYL_AG 1.281706e+06 9818 0.007660
20240424_manuel_lessi_0129_ScanRegion3_rep_4 19790425 20240424_manuel_lessi_0129_ScanRegion3 ARYL_AG_CTL08A CTL08A ARYL_AG 2.090364e+06 23465 0.011225

65 rows × 8 columns

In [571]:
check_area_df[check_area_df.Condition.isin(['RET_AG', 'DMSO'])][['area', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[571]:
area
Condition
DMSO 49127353.0
RET_AG 11112634.0
In [572]:
n_nuclei[n_nuclei.Condition.isin(['RET_AG', 'DMSO'])][[0, 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[572]:
0
Condition
DMSO 60234.0
RET_AG 5523.0

Statistical testing for SOX2+ proportions¶

In [573]:
sox2_pos_cells['total'] = sox2_pos_cells['ImageName_ScanRegion_rep'].map({i:j for i, j in zip(n_nuclei['ImageName_ScanRegion_rep'], n_nuclei[0])})
In [574]:
sox2_pos_cells_CTL08A = sox2_pos_cells[sox2_pos_cells['Line'] == 'CTL08A']

sox2_pos_cells_CTL08A["prop"] = sox2_pos_cells_CTL08A["otsu_1"] / sox2_pos_cells_CTL08A["total"]
sox2_pos_cells_CTL08A["logit_prop"] = np.log(sox2_pos_cells_CTL08A["prop"] / (1 - sox2_pos_cells_CTL08A["prop"]))

model_CTL08A = smf.ols(
    "logit_prop ~ C(Condition, Treatment(reference='DMSO'))",
    data=sox2_pos_cells_CTL08A
).fit()


print(model_CTL08A.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:             logit_prop   R-squared:                       0.565
Model:                            OLS   Adj. R-squared:                  0.309
Method:                 Least Squares   F-statistic:                     2.201
Date:                Mon, 02 Mar 2026   Prob (F-statistic):             0.0496
Time:                        15:07:52   Log-Likelihood:                -14.374
No. Observations:                  36   AIC:                             56.75
Df Residuals:                      22   BIC:                             78.92
Df Model:                          13                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                -2.1951      0.266     -8.240      0.000      -2.748      -1.643
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]      0.2963      0.377      0.786      0.440      -0.485       1.078
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]      0.2565      0.337      0.761      0.455      -0.442       0.955
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]    -0.1645      0.377     -0.437      0.667      -0.946       0.617
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]      0.4915      0.421      1.167      0.256      -0.382       1.365
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]     0.5876      0.421      1.395      0.177      -0.286       1.461
C(Condition, Treatment(reference='DMSO'))[T.GC_AG]        0.7627      0.421      1.811      0.084      -0.111       1.636
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]       0.8711      0.421      2.068      0.051      -0.002       1.745
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]        0.2757      0.377      0.732      0.472      -0.506       1.057
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]       0.8440      0.533      1.584      0.127      -0.261       1.949
C(Condition, Treatment(reference='DMSO'))[T.RET_AG]       1.3298      0.377      3.529      0.002       0.548       2.111
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]      0.0072      0.377      0.019      0.985      -0.774       0.789
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]     -0.0771      0.421     -0.183      0.856      -0.951       0.796
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]     0.3515      0.421      0.835      0.413      -0.522       1.225
==============================================================================
Omnibus:                        7.041   Durbin-Watson:                   1.978
Prob(Omnibus):                  0.030   Jarque-Bera (JB):                6.343
Skew:                          -0.638   Prob(JB):                       0.0419
Kurtosis:                       4.613   Cond. No.                         14.0
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
/tmp/ipykernel_3702104/2300495477.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  sox2_pos_cells_CTL08A["prop"] = sox2_pos_cells_CTL08A["otsu_1"] / sox2_pos_cells_CTL08A["total"]
/tmp/ipykernel_3702104/2300495477.py:4: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  sox2_pos_cells_CTL08A["logit_prop"] = np.log(sox2_pos_cells_CTL08A["prop"] / (1 - sox2_pos_cells_CTL08A["prop"]))
In [575]:
sox2_pos_cells_CTL04E = sox2_pos_cells[sox2_pos_cells['Line'] == 'CTL04E']

sox2_pos_cells_CTL04E["prop"] = sox2_pos_cells_CTL04E["otsu_1"] / sox2_pos_cells_CTL04E["total"]
sox2_pos_cells_CTL04E["logit_prop"] = np.log(sox2_pos_cells_CTL04E["prop"] / (1 - sox2_pos_cells_CTL04E["prop"]))

model_CTL04E = smf.ols(
    "logit_prop ~ C(Condition, Treatment(reference='DMSO'))",
    data=sox2_pos_cells_CTL04E,
    freq_weights=sox2_pos_cells_CTL04E["total"]
).fit()


print(model_CTL04E.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:             logit_prop   R-squared:                       0.379
Model:                            OLS   Adj. R-squared:                 -0.023
Method:                 Least Squares   F-statistic:                    0.9439
Date:                Mon, 02 Mar 2026   Prob (F-statistic):              0.525
Time:                        15:07:53   Log-Likelihood:                -9.7081
No. Observations:                  29   AIC:                             43.42
Df Residuals:                      17   BIC:                             59.82
Df Model:                          11                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                -1.5235      0.312     -4.878      0.000      -2.182      -0.865
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]      0.4697      0.442      1.063      0.302      -0.462       1.402
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]     -0.2268      0.403     -0.563      0.581      -1.078       0.624
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]    -0.0049      0.403     -0.012      0.991      -0.856       0.846
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]     -0.1204      0.442     -0.273      0.788      -1.052       0.811
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]     0.0034      0.403      0.008      0.993      -0.847       0.854
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]      -0.2240      0.403     -0.555      0.586      -1.075       0.627
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]       -0.3528      0.442     -0.799      0.436      -1.285       0.579
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]       0.2639      0.403      0.655      0.522      -0.587       1.115
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]     -0.3037      0.442     -0.688      0.501      -1.236       0.628
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]      0.3429      0.403      0.850      0.407      -0.508       1.194
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]     0.5097      0.541      0.942      0.359      -0.632       1.651
==============================================================================
Omnibus:                        4.068   Durbin-Watson:                   1.955
Prob(Omnibus):                  0.131   Jarque-Bera (JB):                2.536
Skew:                          -0.471   Prob(JB):                        0.281
Kurtosis:                       4.100   Cond. No.                         14.2
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
/tmp/ipykernel_3702104/4102023209.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  sox2_pos_cells_CTL04E["prop"] = sox2_pos_cells_CTL04E["otsu_1"] / sox2_pos_cells_CTL04E["total"]
/tmp/ipykernel_3702104/4102023209.py:4: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  sox2_pos_cells_CTL04E["logit_prop"] = np.log(sox2_pos_cells_CTL04E["prop"] / (1 - sox2_pos_cells_CTL04E["prop"]))
/group/testa/Users/alessia.valenti/NewImageAnalysis/.local/lib/python3.10/site-packages/statsmodels/base/model.py:130: ValueWarning: unknown kwargs ['freq_weights']
  warnings.warn(msg, ValueWarning)
In [576]:
resid_CTL08A = model_CTL08A.resid

stats.probplot(resid_CTL08A, dist="norm", plot=plt)
plt.show()
In [577]:
resid_CTL04E = model_CTL04E.resid

stats.probplot(resid_CTL04E, dist="norm", plot=plt)
plt.show()
In [578]:
pvals_CTL08A = model_CTL08A.pvalues[1:]  # exclude intercept
corrected_pvals_CTL08A = multipletests(pvals_CTL08A, method='fdr_bh')[1]

df_results_CTL08A = pd.concat([model_CTL08A.params, pd.Series(corrected_pvals_CTL08A, index=model_CTL08A.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL08A.columns = ['intercept', 'pvalue']
In [579]:
pvals_CTL04E = model_CTL04E.pvalues[1:]  # exclude intercept
corrected_pvals_CTL04E = multipletests(pvals_CTL04E, method='fdr_bh')[1]

df_results_CTL04E = pd.concat([model_CTL04E.params, pd.Series(corrected_pvals_CTL04E, index=model_CTL04E.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL04E.columns = ['intercept', 'pvalue']
In [580]:
pvals = pd.concat([df_results_CTL08A['pvalue'], df_results_CTL04E['pvalue']], axis = 1)
pvals.columns = ['CTL08A', 'CTL04E']
pvals.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in pvals.index]
pvals
Out[580]:
CTL08A CTL04E
ANDR_AG 0.613721 0.805461
ARYL_AG 0.613721 0.805461
ARYL_INH 0.787909 0.993399
ESTR_AG 0.554127 0.963623
ESTR_INH 0.459976 0.993399
GC_AG 0.363385 NaN
GC_INH 0.328821 0.805461
LX_AG 0.613721 0.805461
LX_INH 0.414184 0.805461
RET_AG 0.024514 NaN
RET_INH 0.984871 0.805461
THYR_AG 0.927832 0.805461
THYR_INH 0.613721 0.805461
In [581]:
intercepts = pd.concat([df_results_CTL08A['intercept'], df_results_CTL04E['intercept']], axis = 1)
intercepts.columns = ['CTL08A', 'CTL04E']
intercepts.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in intercepts.index]
intercepts = (np.exp(intercepts) - 1) * 100 # The odds of being SOX2-positive are increased/decreased by this percentage compared to DMSO.
intercepts
Out[581]:
CTL08A CTL04E
ANDR_AG 34.488239 59.950465
ARYL_AG 29.237804 -20.293282
ARYL_INH -15.165839 -0.484287
ESTR_AG 63.479583 -11.345261
ESTR_INH 79.974485 0.339119
GC_AG 114.412482 NaN
GC_INH 138.960827 -20.067131
LX_AG 31.739861 -29.725497
LX_INH 132.570815 30.203388
RET_AG 278.009841 NaN
RET_INH 0.725216 -26.194311
THYR_AG -7.419489 40.904062
THYR_INH 42.123558 66.475574
In [582]:
fig, ax = plt.subplots(figsize = (4,7))
intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")
intercepts = intercepts[pvals < 0.05].fillna('')
sns.heatmap(pvals, annot=intercepts, fmt='', cmap='Greens_r', cbar_kws={'label': 'p-value'}, vmin=0, vmax=0.05, ax = ax)
plt.title('Condition vs DMSO\n')
plt.savefig('../../../../endpoints_figures/endpoints_organoids_SOX2pos_test.png', dpi=300, bbox_inches='tight')
/tmp/ipykernel_3702104/2823708458.py:2: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead.
  intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")

SOX2+ / organoid area¶

In [583]:
fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = density_SOX2[(density_SOX2['Line'] == 'CTL08A') & 
                                 (density_SOX2['Condition'] == cond)]['otsu_1']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = density_SOX2[(density_SOX2['Condition'] == 'DMSO') & 
                                       (density_SOX2['Line'] == 'CTL08A')]['otsu_1'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=density_SOX2[(density_SOX2['Line'] == 'CTL08A')],
    y='otsu_1', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = density_SOX2[(density_SOX2['Line'] == 'CTL04E') & 
                                 (density_SOX2['Condition'] == cond)]['otsu_1']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = density_SOX2[(density_SOX2['Condition'] == 'DMSO') & 
                                       (density_SOX2['Line'] == 'CTL04E')]['otsu_1'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=density_SOX2[density_SOX2['Line'] == 'CTL04E'],
    y='otsu_1', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('SOX2+ nuclei / organoid area (um²)')
ax[1].set_ylabel('SOX2+ nuclei / organoid area (um²)')

plt.savefig('../../../../endpoints_figures/SOX2_density_otsu_1_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/SOX2_density_otsu_1_per_condition.png', dpi=300, bbox_inches='tight')
In [584]:
density_SOX2[density_SOX2.Condition.isin(['RET_AG', 'DMSO'])][['otsu_1', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[584]:
otsu_1
Condition
DMSO 0.130543
RET_AG 0.200523

Channel 0 - TUJ1¶

In [585]:
mask_file_df_mix_1 = mask_file_df_mix_1.merge(sample_sheet, left_on='ImageName_ScanRegion', right_on='ImageName_ScanRegion', how='left')
mask_file_df_mix_1
Out[585]:
level_0 level_1 area label intensity_mean intensity_min intensity_max OriginalImageName ImageName_ScanRegion ImageName_ScanRegion_Rep Path Scan Area Condition Line OPA Paraffin Block ID MIX ImageName Condition_Line
0 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_0_c... 41353242.0 1 2417.670006 1451.0 24120.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_0 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 RET_INH CTL04E 18.0 65.0 1.0 20240424_manuel_lessi_0134 RET_INH_CTL04E
1 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_1_c... 16657221.0 1 3091.305189 1683.0 18027.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_1 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 RET_INH CTL04E 18.0 65.0 1.0 20240424_manuel_lessi_0134 RET_INH_CTL04E
2 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion0_rep_2_c... 44113333.0 1 3377.073590 1493.0 27952.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_2 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion0 RET_INH CTL04E 18.0 65.0 1.0 20240424_manuel_lessi_0134 RET_INH_CTL04E
3 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion1_rep_0_c... 10098868.0 1 2645.539375 1110.0 27468.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion1 20240424_manuel_lessi_0134_ScanRegion1_rep_0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0134_ScanRegion1_rep_1_c... 7030992.0 1 3937.014572 1941.0 25144.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion1 20240424_manuel_lessi_0134_ScanRegion1_rep_1 NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
110 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion4_rep_0_c... 31563732.0 1 5826.471794 2338.0 65535.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_0 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 ESTR_INH CTL08A 12.0 32.0 1.0 20240424_manuel_lessi_0128 ESTR_INH_CTL08A
111 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion4_rep_1_c... 15937774.0 1 4450.546162 1907.0 34600.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_1 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 ESTR_INH CTL08A 12.0 32.0 1.0 20240424_manuel_lessi_0128 ESTR_INH_CTL08A
112 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_0_c... 27625720.0 1 8458.966141 3148.0 65535.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_0 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ANDR_AG CTL08A 12.0 34.0 1.0 20240424_manuel_lessi_0128 ANDR_AG_CTL08A
113 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_1_c... 24213189.0 1 8076.440269 3291.0 64452.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_1 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ANDR_AG CTL08A 12.0 34.0 1.0 20240424_manuel_lessi_0128 ANDR_AG_CTL08A
114 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_2_c... 40133175.0 1 4824.591142 1926.0 50550.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_2 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ANDR_AG CTL08A 12.0 34.0 1.0 20240424_manuel_lessi_0128 ANDR_AG_CTL08A

115 rows × 19 columns

In [586]:
mask_file_df_mix_1.set_index('level_1', inplace=True)
In [587]:
mask_file_df_mix_1['Line'] = mask_file_df_mix_1['Condition_Line'].apply(lambda x: x.split('_')[-1] if pd.notnull(x) else x)
In [588]:
mask_file_df_mix_1['organoid_area'] = mask_file_df_mix_1['ImageName_ScanRegion_Rep'].map({i:j for i, j in zip(organoid_areas.index, organoid_areas['area'])})
In [589]:
mask_file_df_mix_1['organoid_area_um2'] = mask_file_df_mix_1['organoid_area'] * (px_size ** 2)
mask_file_df_mix_1['area_um2'] = mask_file_df_mix_1['area'] * (px_size ** 2)
In [590]:
n_nuclei = pd.read_csv('../tables/n_nuclei_mix1.csv')
n_nuclei['ImageName'] = n_nuclei['ImageName'].str.replace('_channel_1', '')
In [591]:
mask_file_df_mix_1['n_nuclei'] = mask_file_df_mix_1['ImageName_ScanRegion_Rep'].map({i:j for i, j in zip(n_nuclei['ImageName'], n_nuclei['count'])})
In [592]:
mask_file_df_mix_1['pos_area_over_nuclei'] = mask_file_df_mix_1['area_um2'] / mask_file_df_mix_1['n_nuclei']
mask_file_df_mix_1['pos_area_over_tot'] = mask_file_df_mix_1['area_um2'] / mask_file_df_mix_1['organoid_area_um2']
In [593]:
mask_file_df_mix_1.to_csv("/group/testa/Project/EndPoints/TPSSU/analysis/measurements_mix_1_ch0_TUJ1.csv")
In [594]:
mask_file_df_mix_1
Out[594]:
level_0 area label intensity_mean intensity_min intensity_max OriginalImageName ImageName_ScanRegion ImageName_ScanRegion_Rep Path ... MIX ImageName Condition_Line Line organoid_area organoid_area_um2 area_um2 n_nuclei pos_area_over_nuclei pos_area_over_tot
level_1
20240424_manuel_lessi_0134_ScanRegion0_rep_0_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 41353242.0 1 2417.670006 1451.0 24120.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_0 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ... 1.0 20240424_manuel_lessi_0134 RET_INH_CTL04E CTL04E 56724590.0 5.991535e+06 4.367936e+06 54097 80.742669 0.729018
20240424_manuel_lessi_0134_ScanRegion0_rep_1_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 16657221.0 1 3091.305189 1683.0 18027.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_1 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ... 1.0 20240424_manuel_lessi_0134 RET_INH_CTL04E CTL04E 24401739.0 2.577434e+06 1.759419e+06 24746 71.099126 0.682624
20240424_manuel_lessi_0134_ScanRegion0_rep_2_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 44113333.0 1 3377.073590 1493.0 27952.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion0 20240424_manuel_lessi_0134_ScanRegion0_rep_2 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ... 1.0 20240424_manuel_lessi_0134 RET_INH_CTL04E CTL04E 55519766.0 5.864275e+06 4.659471e+06 50003 93.183825 0.794552
20240424_manuel_lessi_0134_ScanRegion1_rep_0_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 10098868.0 1 2645.539375 1110.0 27468.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion1 20240424_manuel_lessi_0134_ScanRegion1_rep_0 NaN ... NaN NaN NaN NaN NaN NaN 1.066693e+06 12349 86.378892 NaN
20240424_manuel_lessi_0134_ScanRegion1_rep_1_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 7030992.0 1 3937.014572 1941.0 25144.0 20240424_manuel_lessi_0134 20240424_manuel_lessi_0134_ScanRegion1 20240424_manuel_lessi_0134_ScanRegion1_rep_1 NaN ... NaN NaN NaN NaN NaN NaN 7.426485e+05 12207 60.837923 NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
20240424_manuel_lessi_0128_ScanRegion4_rep_0_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 31563732.0 1 5826.471794 2338.0 65535.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_0 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ... 1.0 20240424_manuel_lessi_0128 ESTR_INH_CTL08A CTL08A 42198648.0 4.457232e+06 3.333919e+06 42538 78.375081 0.747980
20240424_manuel_lessi_0128_ScanRegion4_rep_1_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 15937774.0 1 4450.546162 1907.0 34600.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_1 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ... 1.0 20240424_manuel_lessi_0128 ESTR_INH_CTL08A CTL08A 21487131.0 2.269578e+06 1.683427e+06 17120 98.331038 0.741736
20240424_manuel_lessi_0128_ScanRegion5_rep_0_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 27625720.0 1 8458.966141 3148.0 65535.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_0 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ... 1.0 20240424_manuel_lessi_0128 ANDR_AG_CTL08A CTL08A 35064139.0 3.703650e+06 2.917967e+06 36668 79.578016 0.787862
20240424_manuel_lessi_0128_ScanRegion5_rep_1_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 24213189.0 1 8076.440269 3291.0 64452.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_1 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ... 1.0 20240424_manuel_lessi_0128 ANDR_AG_CTL08A CTL08A 31084653.0 3.283316e+06 2.557518e+06 31370 81.527513 0.778944
20240424_manuel_lessi_0128_ScanRegion5_rep_2_channel_0 /group/testa/Project/EndPoints/TPSSU/analysis/... 40133175.0 1 4824.591142 1926.0 50550.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_2 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ... 1.0 20240424_manuel_lessi_0128 ANDR_AG_CTL08A CTL08A 51887483.0 5.480615e+06 4.239067e+06 44878 94.457565 0.773465

115 rows × 25 columns

Plot positive area/number of nuclei¶

In [595]:
df = mask_file_df_mix_1[mask_file_df_mix_1.ImageName_ScanRegion_Rep.isin(good_tissue_to_keep_limit)]

fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL08A') & 
                                 (df['Condition'] == cond)]['pos_area_over_nuclei']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL08A')]['pos_area_over_nuclei'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[(df['Line'] == 'CTL08A')],
    y='pos_area_over_nuclei', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL04E') & 
                                 (df['Condition'] == cond)]['pos_area_over_nuclei']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL04E')]['pos_area_over_nuclei'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[df['Line'] == 'CTL04E'],
    y='pos_area_over_nuclei', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('TUJ1+ area (um2) / # nuclei')
ax[1].set_ylabel('TUJ1+ area (um2) / # nuclei')

plt.savefig('../../../../endpoints_figures/TUJ1_pos_area_over_nuclei_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/TUJ1_pos_area_over_nuclei_per_condition.png', dpi=300, bbox_inches='tight')
In [596]:
df[df.Condition.isin(['RET_AG', 'DMSO'])][['pos_area_over_nuclei', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[596]:
pos_area_over_nuclei
Condition
DMSO 59.300010
RET_AG 123.506076

Statistical testing for TUJ1 area / number of nuclei¶

In [597]:
mask_file_df_mix_1_CTL04E = mask_file_df_mix_1[mask_file_df_mix_1['Line'] == 'CTL04E']
mask_file_df_mix_1_CTL04E['Condition'] = mask_file_df_mix_1_CTL04E['Condition'].astype('category')
mask_file_df_mix_1_CTL04E["log_area_over_n_nuclei"] = np.log(mask_file_df_mix_1_CTL04E["area_um2"] / mask_file_df_mix_1_CTL04E["n_nuclei"])
mask_file_df_mix_1_CTL04E['log_area_over_n_nuclei'].hist(bins = 10)

model_CTL04E = smf.ols("log_area_over_n_nuclei ~ C(Condition, Treatment(reference='DMSO'))",
                data=mask_file_df_mix_1_CTL04E,
                freq_weights=mask_file_df_mix_1_CTL04E["n_nuclei"]
                ).fit()

print(model_CTL04E.summary())
/tmp/ipykernel_3702104/58368655.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  mask_file_df_mix_1_CTL04E['Condition'] = mask_file_df_mix_1_CTL04E['Condition'].astype('category')
/tmp/ipykernel_3702104/58368655.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  mask_file_df_mix_1_CTL04E["log_area_over_n_nuclei"] = np.log(mask_file_df_mix_1_CTL04E["area_um2"] / mask_file_df_mix_1_CTL04E["n_nuclei"])
/group/testa/Users/alessia.valenti/NewImageAnalysis/.local/lib/python3.10/site-packages/statsmodels/base/model.py:130: ValueWarning: unknown kwargs ['freq_weights']
  warnings.warn(msg, ValueWarning)
                              OLS Regression Results                              
==================================================================================
Dep. Variable:     log_area_over_n_nuclei   R-squared:                       0.346
Model:                                OLS   Adj. R-squared:                 -0.032
Method:                     Least Squares   F-statistic:                    0.9149
Date:                    Mon, 02 Mar 2026   Prob (F-statistic):              0.546
Time:                            15:08:00   Log-Likelihood:                -9.9467
No. Observations:                      31   AIC:                             43.89
Df Residuals:                          19   BIC:                             61.10
Df Model:                              11                                         
Covariance Type:                nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                 4.0803      0.301     13.545      0.000       3.450       4.711
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]      0.6328      0.426      1.486      0.154      -0.259       1.524
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]      0.2016      0.389      0.518      0.610      -0.612       1.016
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]     0.1546      0.389      0.397      0.695      -0.659       0.969
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]     -0.0232      0.426     -0.054      0.957      -0.915       0.868
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]     0.2824      0.389      0.726      0.477      -0.532       1.096
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]      -0.0640      0.389     -0.164      0.871      -0.878       0.750
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]        0.7773      0.389      1.999      0.060      -0.037       1.591
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]       0.0914      0.389      0.235      0.817      -0.723       0.905
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]      0.3163      0.389      0.813      0.426      -0.498       1.130
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]      0.2027      0.389      0.521      0.608      -0.611       1.017
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]     0.2782      0.522      0.533      0.600      -0.814       1.370
==============================================================================
Omnibus:                       18.900   Durbin-Watson:                   2.640
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               36.693
Skew:                           1.242   Prob(JB):                     1.08e-08
Kurtosis:                       7.716   Cond. No.                         14.7
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [598]:
mask_file_df_mix_1_CTL08A = mask_file_df_mix_1[mask_file_df_mix_1['Line'] == 'CTL08A']
mask_file_df_mix_1_CTL08A['Condition'] = mask_file_df_mix_1_CTL08A['Condition'].astype('category')
mask_file_df_mix_1_CTL08A["log_area_over_n_nuclei"] = np.log(mask_file_df_mix_1_CTL08A["area_um2"] / mask_file_df_mix_1_CTL08A["n_nuclei"])
mask_file_df_mix_1_CTL08A['log_area_over_n_nuclei'].hist(bins = 10)

model_CTL08A = smf.ols("log_area_over_n_nuclei ~ C(Condition, Treatment(reference='DMSO'))",
                data=mask_file_df_mix_1_CTL08A,
                freq_weights=mask_file_df_mix_1_CTL08A["n_nuclei"]
                ).fit()

print(model_CTL08A.summary())
                              OLS Regression Results                              
==================================================================================
Dep. Variable:     log_area_over_n_nuclei   R-squared:                       0.526
Model:                                OLS   Adj. R-squared:                  0.289
Method:                     Least Squares   F-statistic:                     2.219
Date:                    Mon, 02 Mar 2026   Prob (F-statistic):             0.0406
Time:                            15:08:01   Log-Likelihood:                0.32798
No. Observations:                      40   AIC:                             27.34
Df Residuals:                          26   BIC:                             50.99
Df Model:                              13                                         
Covariance Type:                nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                 4.0439      0.172     23.530      0.000       3.691       4.397
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]      0.3981      0.243      1.638      0.114      -0.102       0.898
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]      0.4010      0.217      1.845      0.077      -0.046       0.848
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]     0.6730      0.243      2.769      0.010       0.173       1.173
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]      0.6589      0.272      2.425      0.023       0.100       1.217
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]     0.4310      0.272      1.586      0.125      -0.128       0.990
C(Condition, Treatment(reference='DMSO'))[T.GC_AG]        0.3515      0.272      1.293      0.207      -0.207       0.910
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]       0.5384      0.243      2.215      0.036       0.039       1.038
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]        0.4979      0.243      2.048      0.051      -0.002       0.997
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]       0.2920      0.272      1.074      0.292      -0.267       0.851
C(Condition, Treatment(reference='DMSO'))[T.RET_AG]       0.8220      0.243      3.382      0.002       0.322       1.322
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]      0.0887      0.243      0.365      0.718      -0.411       0.588
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]      0.1299      0.243      0.534      0.598      -0.370       0.629
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]    -0.0455      0.243     -0.187      0.853      -0.545       0.454
==============================================================================
Omnibus:                        1.470   Durbin-Watson:                   2.601
Prob(Omnibus):                  0.479   Jarque-Bera (JB):                0.617
Skew:                          -0.193   Prob(JB):                        0.734
Kurtosis:                       3.470   Cond. No.                         14.7
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
/tmp/ipykernel_3702104/4221072985.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  mask_file_df_mix_1_CTL08A['Condition'] = mask_file_df_mix_1_CTL08A['Condition'].astype('category')
/tmp/ipykernel_3702104/4221072985.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  mask_file_df_mix_1_CTL08A["log_area_over_n_nuclei"] = np.log(mask_file_df_mix_1_CTL08A["area_um2"] / mask_file_df_mix_1_CTL08A["n_nuclei"])
/group/testa/Users/alessia.valenti/NewImageAnalysis/.local/lib/python3.10/site-packages/statsmodels/base/model.py:130: ValueWarning: unknown kwargs ['freq_weights']
  warnings.warn(msg, ValueWarning)
In [599]:
resid_CTL08A = model_CTL08A.resid

stats.probplot(resid_CTL08A, dist="norm", plot=plt)
plt.show()
In [600]:
resid_CTL04E = model_CTL04E.resid

stats.probplot(resid_CTL04E, dist="norm", plot=plt)
plt.show()
In [601]:
pvals_CTL08A = model_CTL08A.pvalues[1:]  # exclude intercept
corrected_pvals_CTL08A = multipletests(pvals_CTL08A, method='fdr_bh')[1]

df_results_CTL08A = pd.concat([model_CTL08A.params, pd.Series(corrected_pvals_CTL08A, index=model_CTL08A.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL08A.columns = ['intercept', 'pvalue']
In [602]:
pvals_CTL04E = model_CTL04E.pvalues[1:]  # exclude intercept
corrected_pvals_CTL04E = multipletests(pvals_CTL04E, method='fdr_bh')[1]

df_results_CTL04E = pd.concat([model_CTL04E.params, pd.Series(corrected_pvals_CTL04E, index=model_CTL04E.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL04E.columns = ['intercept', 'pvalue']
In [603]:
pvals = pd.concat([df_results_CTL08A['pvalue'], df_results_CTL04E['pvalue']], axis = 1)
pvals.columns = ['CTL08A', 'CTL04E']
pvals.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in pvals.index]
pvals
Out[603]:
CTL08A CTL04E
ANDR_AG 0.202776 0.845971
ARYL_AG 0.165762 0.956242
ARYL_INH 0.066489 0.956242
ESTR_AG 0.097848 0.957144
ESTR_INH 0.202776 0.956242
GC_AG 0.299312 NaN
GC_INH 0.116107 0.957144
LX_AG 0.131940 0.661754
LX_INH 0.380217 0.957144
RET_AG 0.029726 NaN
RET_INH 0.778107 0.956242
THYR_AG 0.706278 0.956242
THYR_INH 0.852862 0.956242
In [604]:
intercepts = pd.concat([df_results_CTL08A['intercept'], df_results_CTL04E['intercept']], axis = 1)
intercepts.columns = ['CTL08A', 'CTL04E']
intercepts.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in intercepts.index]
intercepts = (np.exp(intercepts) - 1) * 100
intercepts
Out[604]:
CTL08A CTL04E
ANDR_AG 48.892111 88.294004
ARYL_AG 49.334772 22.339423
ARYL_INH 96.018670 16.715867
ESTR_AG 93.257018 -2.292952
ESTR_INH 53.884421 32.630223
GC_AG 42.118838 NaN
GC_INH 71.321014 -6.196268
LX_AG 64.519630 117.548202
LX_INH 33.907897 9.572143
RET_AG 127.500496 NaN
RET_INH 9.270000 37.207111
THYR_AG 13.869399 22.465599
THYR_INH -4.450827 32.070909
In [605]:
fig, ax = plt.subplots(figsize = (4,7))
intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")
intercepts = intercepts[pvals < 0.05].fillna('')
sns.heatmap(pvals, annot=intercepts, fmt='', cmap='Greens_r', cbar_kws={'label': 'p-value'}, vmin=0, vmax=0.05, ax = ax)
plt.title('Condition vs DMSO\n')
plt.savefig('../../../../endpoints_figures/endpoints_organoids_TUJ_n_nuclei_OLS_test.png', dpi=300, bbox_inches='tight')
/tmp/ipykernel_3702104/584137039.py:2: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead.
  intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")

Plot positive area/total area¶

In [606]:
df = mask_file_df_mix_1[mask_file_df_mix_1.ImageName_ScanRegion_Rep.isin(good_tissue_to_keep_limit)]

fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL08A') & 
                                 (df['Condition'] == cond)]['pos_area_over_tot']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL08A')]['pos_area_over_tot'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[(df['Line'] == 'CTL08A')],
    y='pos_area_over_tot', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL04E') & 
                                 (df['Condition'] == cond)]['pos_area_over_tot']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL04E')]['pos_area_over_tot'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[df['Line'] == 'CTL04E'],
    y='pos_area_over_tot', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('TUJ1+ area (um2) / organoid area (um2)', fontsize=15)
ax[1].set_ylabel('TUJ1+ area (um2) / organoid area (um2)', fontsize=15)

plt.savefig('../../../../endpoints_figures/TUJ1_pos_area_over_tot_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/TUJ1_pos_area_over_tot_per_condition.png', dpi=300, bbox_inches='tight')
In [607]:
df[df.Condition.isin(['RET_AG', 'DMSO'])][['pos_area_over_tot', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[607]:
pos_area_over_tot
Condition
DMSO 0.702504
RET_AG 0.669194

Channel 2 - SOX2 - Area¶

In [608]:
SOX2_KI67_mask_file_df_mix_1 = SOX2_KI67_mask_file_df_mix_1.merge(sample_sheet, left_on='ImageName_ScanRegion', right_on='ImageName_ScanRegion', how='left')
SOX2_KI67_mask_file_df_mix_1
Out[608]:
level_0 level_1 area label intensity_mean intensity_min intensity_max OriginalImageName ImageName_ScanRegion ImageName_ScanRegion_Rep Path Scan Area Condition Line OPA Paraffin Block ID MIX ImageName Condition_Line
0 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion0_rep_0_c... 21490093.0 1 3839.355973 1168.0 38274.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion0 20240424_manuel_lessi_0133_ScanRegion0_rep_0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion0_rep_1_c... 14199055.0 1 4477.197476 1226.0 46860.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion0 20240424_manuel_lessi_0133_ScanRegion0_rep_1 NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion0_rep_2_c... 15755642.0 1 3759.473544 1092.0 43784.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion0 20240424_manuel_lessi_0133_ScanRegion0_rep_2 NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion1_rep_0_c... 1037489.0 1 2031.997964 957.0 21498.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion1 20240424_manuel_lessi_0133_ScanRegion1_rep_0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0133_ScanRegion1_rep_1_c... 2023185.0 1 3780.785030 1512.0 38018.0 20240424_manuel_lessi_0133 20240424_manuel_lessi_0133_ScanRegion1 20240424_manuel_lessi_0133_ScanRegion1_rep_1 NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
110 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion4_rep_0_c... 16244663.0 1 4090.208776 1313.0 48739.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_0 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 ESTR_INH CTL08A 12.0 32.0 1.0 20240424_manuel_lessi_0128 ESTR_INH_CTL08A
111 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion4_rep_1_c... 7057712.0 1 4113.493459 1235.0 42141.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion4 20240424_manuel_lessi_0128_ScanRegion4_rep_1 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion4 ESTR_INH CTL08A 12.0 32.0 1.0 20240424_manuel_lessi_0128 ESTR_INH_CTL08A
112 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_0_c... 12963431.0 1 5394.798945 1568.0 44306.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_0 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ANDR_AG CTL08A 12.0 34.0 1.0 20240424_manuel_lessi_0128 ANDR_AG_CTL08A
113 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_1_c... 10831979.0 1 4610.981995 1604.0 40226.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_1 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ANDR_AG CTL08A 12.0 34.0 1.0 20240424_manuel_lessi_0128 ANDR_AG_CTL08A
114 /group/testa/Project/EndPoints/TPSSU/analysis/... 20240424_manuel_lessi_0128_ScanRegion5_rep_2_c... 17819211.0 1 3234.373585 1320.0 37653.0 20240424_manuel_lessi_0128 20240424_manuel_lessi_0128_ScanRegion5 20240424_manuel_lessi_0128_ScanRegion5_rep_2 T:\Project\EndPoints\TPSSU\Pictures\20240424_m... ScanRegion5 ANDR_AG CTL08A 12.0 34.0 1.0 20240424_manuel_lessi_0128 ANDR_AG_CTL08A

115 rows × 19 columns

In [609]:
SOX2_KI67_mask_file_df_mix_1.set_index('level_1', inplace=True)
In [610]:
SOX2_KI67_mask_file_df_mix_1['Line'] = SOX2_KI67_mask_file_df_mix_1['Condition_Line'].apply(lambda x: x.split('_')[-1] if pd.notnull(x) else x)
In [611]:
SOX2_KI67_mask_file_df_mix_1['organoid_area_um2'] = SOX2_KI67_mask_file_df_mix_1['ImageName_ScanRegion_Rep'].map({i:j for i, j in zip(organoid_areas.index, organoid_areas['area'])}) * (px_size ** 2)
SOX2_KI67_mask_file_df_mix_1['area_um2'] = SOX2_KI67_mask_file_df_mix_1['area'] * (px_size ** 2)
In [612]:
SOX2_KI67_mask_file_df_mix_1['n_nuclei'] = SOX2_KI67_mask_file_df_mix_1['ImageName_ScanRegion_Rep'].map({i:j for i, j in zip(n_nuclei['ImageName'], n_nuclei['count'])})
In [613]:
SOX2_KI67_mask_file_df_mix_1['pos_area_over_nuclei'] = SOX2_KI67_mask_file_df_mix_1['area_um2'] / SOX2_KI67_mask_file_df_mix_1['n_nuclei']
SOX2_KI67_mask_file_df_mix_1['pos_area_over_tot'] = SOX2_KI67_mask_file_df_mix_1['area_um2'] / SOX2_KI67_mask_file_df_mix_1['organoid_area_um2']
In [614]:
mask_file_df_mix_1.to_csv("/group/testa/Project/EndPoints/TPSSU/analysis/measurements_mix_1_ch0_TUJ1.csv")

Plot positive area/number of nuclei¶

In [615]:
df = SOX2_KI67_mask_file_df_mix_1[SOX2_KI67_mask_file_df_mix_1.ImageName_ScanRegion_Rep.isin(good_tissue_to_keep_limit)]

fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL08A') & 
                                 (df['Condition'] == cond)]['pos_area_over_nuclei']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL08A')]['pos_area_over_nuclei'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[(df['Line'] == 'CTL08A')],
    y='pos_area_over_nuclei', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL04E') & 
                                 (df['Condition'] == cond)]['pos_area_over_nuclei']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL04E')]['pos_area_over_nuclei'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[df['Line'] == 'CTL04E'],
    y='pos_area_over_nuclei', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('SOX2+ area (um2) / # nuclei')
ax[1].set_ylabel('SOX2+ area (um2) / # nuclei')
Out[615]:
Text(0, 0.5, 'SOX2+ area (um2) / # nuclei')
In [616]:
df[df.Condition.isin(['RET_AG', 'DMSO'])][['pos_area_over_nuclei', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[616]:
pos_area_over_nuclei
Condition
DMSO 36.427254
RET_AG 60.364566

Plot positive area/total area¶

In [617]:
df = SOX2_KI67_mask_file_df_mix_1[SOX2_KI67_mask_file_df_mix_1.ImageName_ScanRegion_Rep.isin(good_tissue_to_keep_limit)]

fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL08A') & 
                                 (df['Condition'] == cond)]['pos_area_over_tot']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL08A')]['pos_area_over_tot'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[(df['Line'] == 'CTL08A')],
    y='pos_area_over_tot', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL04E') & 
                                 (df['Condition'] == cond)]['pos_area_over_tot']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL04E')]['pos_area_over_tot'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[df['Line'] == 'CTL04E'],
    y='pos_area_over_tot', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('SOX2+ area (um2) / organoid area (um2)', fontsize=15)
ax[1].set_ylabel('SOX2+ area (um2) / organoid area (um2)', fontsize=15)
Out[617]:
Text(0, 0.5, 'SOX2+ area (um2) / organoid area (um2)')
In [618]:
df[df.Condition.isin(['RET_AG', 'DMSO'])][['pos_area_over_tot', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[618]:
pos_area_over_tot
Condition
DMSO 0.431540
RET_AG 0.342605

SOX2 / TUJ1 ratio¶

In [619]:
n_nuclei_tot_pos_SOX2_otsu1 = {k: v['otsu_1'] for k, v in n_nuclei_tot_pos_SOX2.items() if 'otsu_1' in v}
In [620]:
df['SOX2+'] = df['ImageName_ScanRegion_Rep'].map(n_nuclei_tot_pos_SOX2_otsu1)
/tmp/ipykernel_3702104/1000197899.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df['SOX2+'] = df['ImageName_ScanRegion_Rep'].map(n_nuclei_tot_pos_SOX2_otsu1)
In [621]:
df["SOX2+_TUJ1_ratio"] = df['SOX2+'] / df['area_um2']
/tmp/ipykernel_3702104/3857021914.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df["SOX2+_TUJ1_ratio"] = df['SOX2+'] / df['area_um2']
In [622]:
fig, ax = plt.subplots(1, 2, figsize=(25, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL08A') & 
                                 (df['Condition'] == cond)]['SOX2+_TUJ1_ratio']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL08A')]['SOX2+_TUJ1_ratio'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[(df['Line'] == 'CTL08A')],
    y='SOX2+_TUJ1_ratio', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL04E') & 
                                 (df['Condition'] == cond)]['SOX2+_TUJ1_ratio']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL04E')]['SOX2+_TUJ1_ratio'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[df['Line'] == 'CTL04E'],
    y='SOX2+_TUJ1_ratio', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('SOX2+_TUJ1_ratio')
ax[1].set_ylabel('SOX2+_TUJ1_ratio')

plt.savefig('../../../../endpoints_figures/SOX2+_TUJ1_ratio_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/SOX2+_TUJ1_ratio_per_condition.png', dpi=300, bbox_inches='tight')

MIX 2¶

Channel 2 - MKI67¶

In [623]:
tot_df_mix2_ch2 = pd.DataFrame()
measurements_files_mix_2_ch2 = [f for f in measurements_files_mix_2 if 'channel_2' in f]

for file in measurements_files_mix_2_ch2:
    df = pd.read_csv(file, index_col=0)
    df['ImageName'] = file.split("/")[-1].split(".")[0]
    tot_df_mix2_ch2 = pd.concat([tot_df_mix2_ch2, df], axis=0)
In [624]:
# tot_df_mix2_ch2.to_csv("/group/testa/Project/EndPoints/TPSSU/analysis/measurements_mix_2_ch2_MKI67.csv")

Thresholding¶

In [625]:
tot_pos_MKI67 = {}
dens_norm_MKI67 = {}
density_MKI67 = {}
n_nuclei = {}
n_nuclei_tot_pos_MKI67 = {}

for file in measurements_files_mix_2_ch2:
    #print(file)
    imageName_scanRegion_rep = file.split('/')[-1].split('.')[0].replace('_channel_2_measures', '')
    imageName_scanRegion = '_'.join(imageName_scanRegion_rep.split('_')[:5])
    
    if imageName_scanRegion_rep in good_tissue_to_keep_limit and imageName_scanRegion in sample_sheet['ImageName_ScanRegion'].values:
        df = pd.read_csv(file, index_col=0)
        
        imageName_scanRegion_rep = file.split('/')[-1].split('.')[0].replace('_channel_2_measures', '')
        density = density_dict[imageName_scanRegion_rep]
        area = area_dict[imageName_scanRegion_rep]
        
        df['ImageName'] = file.split("/")[-1].split(".")[0]
        tot_pos_MKI67[file.split("/")[-1].split(".")[0]] = {}
        dens_norm_MKI67[imageName_scanRegion_rep] = {}
        density_MKI67[imageName_scanRegion_rep] = {}
        n_nuclei_tot_pos_MKI67[imageName_scanRegion_rep] = {}

        min_quantile = np.quantile(df['area'], 0.01)

        max_quantile = np.quantile(df['area'], 0.99)

        df = df[(df['area'] > min_quantile) & (df['area'] < max_quantile)]

        n_nuclei[file.split("/")[-1].split(".")[0]] = df.shape[0]

        otsu_thrs = filters.threshold_multiotsu(np.array(df['intensity_mean']), classes = 4)

        for i, thr in enumerate(otsu_thrs):
            tot_pos_MKI67[file.split("/")[-1].split(".")[0]][f'otsu_{i}'] = np.sum(df['intensity_mean'] > thr) / df.shape[0] * 100
            n_nuclei_tot_pos_MKI67[imageName_scanRegion_rep][f'otsu_{i}'] = np.sum(df['intensity_mean'] > thr)
            dens_norm_MKI67[imageName_scanRegion_rep][f'otsu_{i}'] = tot_pos_MKI67[file.split("/")[-1].split(".")[0]][f'otsu_{i}'] * density
            density_MKI67[imageName_scanRegion_rep][f'otsu_{i}'] = np.sum(df['intensity_mean'] > thr) / area * 100
In [626]:
df_percentages = pd.DataFrame.from_dict(tot_pos_MKI67, orient='index')
df_percentages['ImageName_ScanRegion'] = df_percentages.reset_index()['index'].apply(lambda x: '_'.join(x.split('_')[0:5])).values
df_percentages['ImageName_ScanRegion_rep'] = df_percentages.reset_index()['index'].apply(lambda x: '_'.join(x.split('_')[0:7])).values
df_percentages['Condition_Line'] = df_percentages['ImageName_ScanRegion'].map(sample_dict)
df_percentages = df_percentages[~df_percentages.Condition_Line.isna()]
df_percentages['Line'] = df_percentages['Condition_Line'].apply(lambda x: x.split('_')[-1])
df_percentages['Condition'] = df_percentages['Condition_Line'].str.replace('_CTL08A', '', regex=True)
df_percentages['Condition'] = df_percentages['Condition'].str.replace('_CTL04E', '', regex=True)

#df_percentages.to_csv("/group/testa/Project/EndPoints/TPSSU/analysis/percentages_mix_2_ch2_MKI67_percentages.csv")
In [627]:
KI67_pos = pd.DataFrame.from_dict(n_nuclei_tot_pos_MKI67, orient='index')
KI67_pos['ImageName_ScanRegion'] = KI67_pos.reset_index()['index'].apply(lambda x: '_'.join(x.split('_')[0:5])).values
KI67_pos['ImageName_ScanRegion_rep'] = KI67_pos.reset_index()['index'].apply(lambda x: '_'.join(x.split('_')[0:7])).values
KI67_pos['Condition_Line'] = KI67_pos['ImageName_ScanRegion'].map(sample_dict)
KI67_pos = KI67_pos[~KI67_pos.Condition_Line.isna()]
KI67_pos['Line'] = KI67_pos['Condition_Line'].apply(lambda x: x.split('_')[-1])
KI67_pos['Condition'] = KI67_pos['Condition_Line'].str.replace('_CTL08A', '', regex=True)
KI67_pos['Condition'] = KI67_pos['Condition'].str.replace('_CTL04E', '', regex=True)
In [628]:
density_MKI67 = pd.DataFrame.from_dict(density_MKI67, orient='index').reset_index()
density_MKI67['ImageName_ScanRegion'] = density_MKI67['index'].apply(lambda x: '_'.join(x.split('_')[0:5]))
density_MKI67['ImageName_ScanRegion_rep'] = density_MKI67['index'].apply(lambda x: '_'.join(x.split('_')[0:7]))
density_MKI67['Condition_Line'] = density_MKI67['ImageName_ScanRegion'].map(sample_dict)
density_MKI67 = density_MKI67[~density_MKI67.Condition_Line.isna()]
density_MKI67['Line'] = density_MKI67['Condition_Line'].apply(lambda x: x.split('_')[-1])
density_MKI67['Condition'] = density_MKI67['Condition_Line'].str.replace('_CTL08A', '', regex=True)
density_MKI67['Condition'] = density_MKI67['Condition'].str.replace('_CTL04E', '', regex=True)

print(len(density_MKI67))
#density_MKI67.to_csv("/group/testa/Project/EndPoints/TPSSU/analysis/MKI67_pos_over_area_mix_1_ch2.csv")
74
In [629]:
n_nuclei_df = pd.DataFrame.from_dict(n_nuclei, orient='index').reset_index()
n_nuclei_df['ImageName_ScanRegion'] = n_nuclei_df['index'].apply(lambda x: '_'.join(x.split('_')[0:5]))
n_nuclei_df['ImageName_ScanRegion_rep'] = n_nuclei_df['index'].apply(lambda x: '_'.join(x.split('_')[0:7]))
n_nuclei_df['Condition_Line'] = n_nuclei_df['ImageName_ScanRegion'].map(sample_dict)
n_nuclei_df = n_nuclei_df[~n_nuclei_df.Condition_Line.isna()]
n_nuclei_df['Line'] = n_nuclei_df['Condition_Line'].apply(lambda x: x.split('_')[-1])
n_nuclei_df['Condition'] = n_nuclei_df['Condition_Line'].str.replace('_CTL08A', '', regex=True)
n_nuclei_df['Condition'] = n_nuclei_df['Condition'].str.replace('_CTL04E', '', regex=True)
In [630]:
check_area_df = organoid_areas.loc[KI67_pos.ImageName_ScanRegion_rep.tolist()]
check_area_df
Out[630]:
area ImageName_ScanRegion Condition_Line Line Condition Area_um2 n_nuclei density
20240424_manuel_lessi_0142_ScanRegion2_rep_0 8024395 20240424_manuel_lessi_0142_ScanRegion2 RET_AG_CTL08A CTL08A RET_AG 8.475767e+05 2437 0.002875
20240424_manuel_lessi_0142_ScanRegion2_rep_2 9480571 20240424_manuel_lessi_0142_ScanRegion2 RET_AG_CTL08A CTL08A RET_AG 1.001385e+06 2340 0.002337
20240424_manuel_lessi_0138_ScanRegion1_rep_2 37281437 20240424_manuel_lessi_0138_ScanRegion1 ANDR_INH_CTL04E CTL04E ANDR_INH 3.937852e+06 30867 0.007839
20240424_manuel_lessi_0140_ScanRegion4_rep_1 55908114 20240424_manuel_lessi_0140_ScanRegion4 THYR_INH_CTL08A CTL08A THYR_INH 5.905295e+06 72472 0.012272
20240424_manuel_lessi_0140_ScanRegion5_rep_2 41670728 20240424_manuel_lessi_0140_ScanRegion5 THYR_AG_CTL08A CTL08A THYR_AG 4.401471e+06 58972 0.013398
... ... ... ... ... ... ... ... ...
20240424_manuel_lessi_0141_ScanRegion3_rep_1 8113209 20240424_manuel_lessi_0141_ScanRegion3 DMSO_CTL04E CTL04E DMSO 8.569577e+05 9105 0.010625
20240424_manuel_lessi_0138_ScanRegion5_rep_1 60950304 20240424_manuel_lessi_0138_ScanRegion5 ARYL_AG_CTL04E CTL04E ARYL_AG 6.437876e+06 63198 0.009817
20240424_manuel_lessi_0138_ScanRegion3_rep_1 16957648 20240424_manuel_lessi_0138_ScanRegion3 ARYL_AG_CTL08A CTL08A ARYL_AG 1.791152e+06 15879 0.008865
20240424_manuel_lessi_0138_ScanRegion1_rep_0 28072509 20240424_manuel_lessi_0138_ScanRegion1 ANDR_INH_CTL04E CTL04E ANDR_INH 2.965159e+06 26167 0.008825
20240424_manuel_lessi_0140_ScanRegion2_rep_0 7130564 20240424_manuel_lessi_0140_ScanRegion2 THYR_INH_CTL04E CTL04E THYR_INH 7.531658e+05 6079 0.008071

74 rows × 8 columns

In [631]:
check_area_df[check_area_df.Condition.isin(['RET_AG', 'DMSO'])][['area', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[631]:
area
Condition
DMSO 50055339.0
RET_AG 9480571.0

Plots¶

Percentages¶

In [632]:
mix2_img_names = tot_df_mix2_ch2['ImageName'].apply(lambda x: '_'.join(x.split('_')[0:4])).value_counts().index.tolist()
mix2_img_names
Out[632]:
['20240424_manuel_lessi_0141',
 '20240424_manuel_lessi_0143',
 '20240424_manuel_lessi_0140',
 '20240424_manuel_lessi_0138',
 '20240424_manuel_lessi_0137',
 '20240424_manuel_lessi_0139',
 '20240424_manuel_lessi_0142']
In [633]:
check_qc_ch2['ImageName_ScanRegion'] = check_qc_ch2['Reference Image Report'].apply(lambda x: '_'.join(x.split('_')[0:5]))
check_qc_ch2['ImageName_ScanRegion_Rep'] = check_qc_ch2['Reference Image Report'].apply(lambda x: '_'.join(x.split('_')[0:7]))
check_qc_ch2['ImageName'] = check_qc_ch2['Reference Image Report'].apply(lambda x: '_'.join(x.split('_')[0:4]))
check_qc_ch2_mix2 = check_qc_ch2[check_qc_ch2['ImageName'].isin(mix2_img_names)]
check_qc_ch2_mix2
Out[633]:
Reference Image Report general QC (keep or not) ImageName_ScanRegion_Rep ImageName_ScanRegion ImageName_ScanRegion_rep ImageName
131 20240424_manuel_lessi_0137_ScanRegion0_rep_0_c... LIMIT 20240424_manuel_lessi_0137_ScanRegion0_rep_0 20240424_manuel_lessi_0137_ScanRegion0 20240424_manuel_lessi_0137_ScanRegion0_rep_0 20240424_manuel_lessi_0137
132 20240424_manuel_lessi_0137_ScanRegion1_rep_0_c... NO 20240424_manuel_lessi_0137_ScanRegion1_rep_0 20240424_manuel_lessi_0137_ScanRegion1 20240424_manuel_lessi_0137_ScanRegion1_rep_0 20240424_manuel_lessi_0137
133 20240424_manuel_lessi_0137_ScanRegion1_rep_1_c... YES 20240424_manuel_lessi_0137_ScanRegion1_rep_1 20240424_manuel_lessi_0137_ScanRegion1 20240424_manuel_lessi_0137_ScanRegion1_rep_1 20240424_manuel_lessi_0137
134 20240424_manuel_lessi_0137_ScanRegion1_rep_2_c... NO 20240424_manuel_lessi_0137_ScanRegion1_rep_2 20240424_manuel_lessi_0137_ScanRegion1 20240424_manuel_lessi_0137_ScanRegion1_rep_2 20240424_manuel_lessi_0137
135 20240424_manuel_lessi_0137_ScanRegion2_rep_0_c... YES 20240424_manuel_lessi_0137_ScanRegion2_rep_0 20240424_manuel_lessi_0137_ScanRegion2 20240424_manuel_lessi_0137_ScanRegion2_rep_0 20240424_manuel_lessi_0137
... ... ... ... ... ... ...
235 20240424_manuel_lessi_0143_ScanRegion4_rep_1_c... YES 20240424_manuel_lessi_0143_ScanRegion4_rep_1 20240424_manuel_lessi_0143_ScanRegion4 20240424_manuel_lessi_0143_ScanRegion4_rep_1 20240424_manuel_lessi_0143
236 20240424_manuel_lessi_0143_ScanRegion4_rep_2_c... YES 20240424_manuel_lessi_0143_ScanRegion4_rep_2 20240424_manuel_lessi_0143_ScanRegion4 20240424_manuel_lessi_0143_ScanRegion4_rep_2 20240424_manuel_lessi_0143
237 20240424_manuel_lessi_0143_ScanRegion5_rep_0_c... YES 20240424_manuel_lessi_0143_ScanRegion5_rep_0 20240424_manuel_lessi_0143_ScanRegion5 20240424_manuel_lessi_0143_ScanRegion5_rep_0 20240424_manuel_lessi_0143
238 20240424_manuel_lessi_0143_ScanRegion5_rep_1_c... YES 20240424_manuel_lessi_0143_ScanRegion5_rep_1 20240424_manuel_lessi_0143_ScanRegion5 20240424_manuel_lessi_0143_ScanRegion5_rep_1 20240424_manuel_lessi_0143
239 20240424_manuel_lessi_0143_ScanRegion5_rep_2_c... YES 20240424_manuel_lessi_0143_ScanRegion5_rep_2 20240424_manuel_lessi_0143_ScanRegion5 20240424_manuel_lessi_0143_ScanRegion5_rep_2 20240424_manuel_lessi_0143

109 rows × 6 columns

In [634]:
check_qc_ch2_mix2['general QC (keep or not)'].value_counts()
Out[634]:
general QC (keep or not)
YES      60
NO       35
LIMIT    14
Name: count, dtype: int64
In [635]:
to_remove = check_qc_ch2_mix2[check_qc_ch2_mix2['general QC (keep or not)'] == 'NO']['ImageName_ScanRegion_Rep'].tolist()
len(to_remove)
Out[635]:
35
In [636]:
df_percentages = df_percentages[~df_percentages['ImageName_ScanRegion_rep'].isin(to_remove)]
len(df_percentages)
Out[636]:
49
In [637]:
KI67_pos = KI67_pos[~KI67_pos['ImageName_ScanRegion_rep'].isin(to_remove)]
len(KI67_pos)
Out[637]:
49
In [638]:
n_nuclei_df = n_nuclei_df[~n_nuclei_df['ImageName_ScanRegion_rep'].isin(to_remove)]
len(n_nuclei_df)
Out[638]:
49
In [639]:
order = df_percentages['Condition'].tolist()
order = list(dict.fromkeys(order))  # Remove duplicates while preserving order
order.remove('DMSO')
order.sort()
order = ['DMSO'] + order
In [640]:
fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df_percentages[(df_percentages['Line'] == 'CTL08A') & 
                                 (df_percentages['Condition'] == cond)]['otsu_1']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df_percentages[(df_percentages['Condition'] == 'DMSO') & 
                                       (df_percentages['Line'] == 'CTL08A')]['otsu_1'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df_percentages[(df_percentages['Line'] == 'CTL08A')],
    y='otsu_1', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df_percentages[(df_percentages['Line'] == 'CTL04E') & 
                                 (df_percentages['Condition'] == cond)]['otsu_1']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df_percentages[(df_percentages['Condition'] == 'DMSO') & 
                                       (df_percentages['Line'] == 'CTL04E')]['otsu_1'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df_percentages[df_percentages['Line'] == 'CTL04E'],
    y='otsu_1', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('KI67+ nuclei (%)', fontsize=15)
ax[1].set_ylabel('KI67+ nuclei (%)', fontsize=15)

plt.savefig('../../../../endpoints_figures/MKI67_pos_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/MKI67_pos_per_condition.png', dpi=300, bbox_inches='tight')
In [641]:
df_percentages[df_percentages.Condition.isin(['RET_AG', 'DMSO'])][['otsu_1', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[641]:
otsu_1
Condition
DMSO 2.812748
RET_AG 4.684807
In [642]:
KI67_pos[KI67_pos.Condition.isin(['RET_AG', 'DMSO'])][['otsu_1', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[642]:
otsu_1
Condition
DMSO 660.0
RET_AG 110.5
In [643]:
n_nuclei_df[n_nuclei_df.Condition.isin(['RET_AG', 'DMSO'])][[0, 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[643]:
0
Condition
DMSO 42327.0
RET_AG 2339.5

Statistical testing for KI67+ proportions¶

In [644]:
KI67_pos['total'] = KI67_pos['ImageName_ScanRegion_rep'].map({i:j for i, j in zip(n_nuclei_df['ImageName_ScanRegion_rep'], n_nuclei_df[0])})
In [645]:
KI67_pos_CTL08A = KI67_pos[KI67_pos['Line'] == 'CTL08A']

KI67_pos_CTL08A["prop"] = KI67_pos_CTL08A["otsu_1"] / KI67_pos_CTL08A["total"]
KI67_pos_CTL08A["logit_prop"] = np.log(KI67_pos_CTL08A["prop"] / (1 - KI67_pos_CTL08A["prop"]))

model_CTL08A = smf.ols(
    "logit_prop ~ C(Condition, Treatment(reference='DMSO'))",
    data=KI67_pos_CTL08A,
    freq_weights=KI67_pos_CTL08A["total"]
).fit()


print(model_CTL08A.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:             logit_prop   R-squared:                       0.568
Model:                            OLS   Adj. R-squared:                  0.167
Method:                 Least Squares   F-statistic:                     1.417
Date:                Mon, 02 Mar 2026   Prob (F-statistic):              0.263
Time:                        15:08:52   Log-Likelihood:                -7.4512
No. Observations:                  28   AIC:                             42.90
Df Residuals:                      14   BIC:                             61.55
Df Model:                          13                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                -4.1452      0.447     -9.283      0.000      -5.103      -3.188
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]      0.5951      0.631      0.942      0.362      -0.759       1.950
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]     1.3577      0.631      2.150      0.050       0.003       2.712
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]      0.7473      0.499      1.497      0.157      -0.323       1.818
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]      0.7175      0.547      1.312      0.211      -0.455       1.890
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]     0.8569      0.547      1.567      0.139      -0.316       2.030
C(Condition, Treatment(reference='DMSO'))[T.GC_AG]        0.3481      0.547      0.637      0.535      -0.825       1.521
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]       1.1186      0.516      2.170      0.048       0.013       2.224
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]        0.3820      0.516      0.741      0.471      -0.724       1.488
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]       0.9965      0.547      1.822      0.090      -0.176       2.169
C(Condition, Treatment(reference='DMSO'))[T.RET_AG]       1.0435      0.547      1.908      0.077      -0.129       2.216
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]      0.1727      0.547      0.316      0.757      -1.000       1.346
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]      0.3073      0.547      0.562      0.583      -0.866       1.480
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]     0.0865      0.631      0.137      0.893      -1.268       1.441
==============================================================================
Omnibus:                        0.516   Durbin-Watson:                   2.415
Prob(Omnibus):                  0.773   Jarque-Bera (JB):                0.087
Skew:                          -0.130   Prob(JB):                        0.957
Kurtosis:                       3.085   Cond. No.                         21.0
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
/tmp/ipykernel_3702104/2826624957.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  KI67_pos_CTL08A["prop"] = KI67_pos_CTL08A["otsu_1"] / KI67_pos_CTL08A["total"]
/tmp/ipykernel_3702104/2826624957.py:4: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  KI67_pos_CTL08A["logit_prop"] = np.log(KI67_pos_CTL08A["prop"] / (1 - KI67_pos_CTL08A["prop"]))
/group/testa/Users/alessia.valenti/NewImageAnalysis/.local/lib/python3.10/site-packages/statsmodels/base/model.py:130: ValueWarning: unknown kwargs ['freq_weights']
  warnings.warn(msg, ValueWarning)
In [646]:
KI67_pos_CTL04E = KI67_pos[KI67_pos['Line'] == 'CTL04E']

KI67_pos_CTL04E["prop"] = KI67_pos_CTL04E["otsu_1"] / KI67_pos_CTL04E["total"]
KI67_pos_CTL04E["logit_prop"] = np.log(KI67_pos_CTL04E["prop"] / (1 - KI67_pos_CTL04E["prop"]))

model_CTL04E = smf.ols(
    "logit_prop ~ C(Condition, Treatment(reference='DMSO'))",
    data=KI67_pos_CTL04E,
    freq_weights=KI67_pos_CTL04E["total"]
).fit()


print(model_CTL04E.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:             logit_prop   R-squared:                       0.910
Model:                            OLS   Adj. R-squared:                  0.821
Method:                 Least Squares   F-statistic:                     10.14
Date:                Mon, 02 Mar 2026   Prob (F-statistic):           0.000538
Time:                        15:08:52   Log-Likelihood:                 1.4378
No. Observations:                  21   AIC:                             19.12
Df Residuals:                      10   BIC:                             30.61
Df Model:                          10                                         
Covariance Type:            nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                -3.3823      0.232    -14.608      0.000      -3.898      -2.866
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]      0.1183      0.401      0.295      0.774      -0.775       1.012
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]     0.0422      0.327      0.129      0.900      -0.687       0.772
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]     0.7196      0.299      2.407      0.037       0.054       1.386
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]     -0.9889      0.401     -2.466      0.033      -1.883      -0.095
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]      -1.9767      0.327     -6.037      0.000      -2.706      -1.247
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]       -0.6295      0.299     -2.106      0.061      -1.296       0.037
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]      -0.6248      0.299     -2.090      0.063      -1.291       0.041
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]     -0.3268      0.327     -0.998      0.342      -1.056       0.403
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]     -0.7444      0.401     -1.856      0.093      -1.638       0.149
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]     0.2459      0.401      0.613      0.553      -0.648       1.140
==============================================================================
Omnibus:                        2.967   Durbin-Watson:                   2.076
Prob(Omnibus):                  0.227   Jarque-Bera (JB):                1.304
Skew:                           0.230   Prob(JB):                        0.521
Kurtosis:                       4.131   Cond. No.                         11.9
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
/tmp/ipykernel_3702104/2466720807.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  KI67_pos_CTL04E["prop"] = KI67_pos_CTL04E["otsu_1"] / KI67_pos_CTL04E["total"]
/tmp/ipykernel_3702104/2466720807.py:4: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  KI67_pos_CTL04E["logit_prop"] = np.log(KI67_pos_CTL04E["prop"] / (1 - KI67_pos_CTL04E["prop"]))
/group/testa/Users/alessia.valenti/NewImageAnalysis/.local/lib/python3.10/site-packages/statsmodels/base/model.py:130: ValueWarning: unknown kwargs ['freq_weights']
  warnings.warn(msg, ValueWarning)
In [647]:
resid_CTL08A = model_CTL08A.resid

stats.probplot(resid_CTL08A, dist="norm", plot=plt)
plt.show()
In [648]:
resid_CTL04E = model_CTL04E.resid

stats.probplot(resid_CTL04E, dist="norm", plot=plt)
plt.show()
In [649]:
pvals_CTL08A = model_CTL08A.pvalues[1:]  # exclude intercept
corrected_pvals_CTL08A = multipletests(pvals_CTL08A, method='fdr_bh')[1]

df_results_CTL08A = pd.concat([model_CTL08A.params, pd.Series(corrected_pvals_CTL08A, index=model_CTL08A.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL08A.columns = ['intercept', 'pvalue']
In [650]:
pvals_CTL04E = model_CTL04E.pvalues[1:]  # exclude intercept
corrected_pvals_CTL04E = multipletests(pvals_CTL04E, method='fdr_bh')[1]

df_results_CTL04E = pd.concat([model_CTL04E.params, pd.Series(corrected_pvals_CTL04E, index=model_CTL04E.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL04E.columns = ['intercept', 'pvalue']
In [651]:
pvals = pd.concat([df_results_CTL08A['pvalue'], df_results_CTL04E['pvalue']], axis = 1)
pvals.columns = ['CTL08A', 'CTL04E']
pvals.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in pvals.index]
pvals
Out[651]:
CTL08A CTL04E
ANDR_AG 0.588199 0.859964
ANDR_INH 0.292076 0.900052
ARYL_AG 0.339398 NaN
ESTR_AG 0.391179 0.122862
ESTR_INH 0.339398 NaN
GC_AG 0.689105 NaN
GC_INH 0.292076 0.001258
LX_AG 0.680433 0.126244
LX_INH 0.292076 0.126244
RET_AG 0.292076 NaN
RET_INH 0.819911 0.488274
THYR_AG 0.689105 0.155187
THYR_INH 0.892963 0.691742
ARYL_INH NaN 0.122862
In [652]:
intercepts = pd.concat([df_results_CTL08A['intercept'], df_results_CTL04E['intercept']], axis = 1)
intercepts.columns = ['CTL08A', 'CTL04E']
intercepts.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in intercepts.index]
intercepts = (np.exp(intercepts) - 1) * 100 # The odds of being KI67-positive are increased/decreased by this percentage compared to DMSO.
In [653]:
fig, ax = plt.subplots(figsize = (4,7))
intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")
intercepts = intercepts[pvals < 0.05].fillna('')
sns.heatmap(pvals, annot=intercepts, fmt='', cmap='Greens_r', cbar_kws={'label': 'p-value'}, vmin=0, vmax=0.05, ax = ax)
plt.title('Condition vs DMSO\n')
plt.savefig('../../../../endpoints_figures/endpoints_organoids_KI67pos_test.png', dpi=300, bbox_inches='tight')
/tmp/ipykernel_3702104/4000860394.py:2: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead.
  intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")

MKI67+ / organoid area¶

In [654]:
df = density_MKI67[density_MKI67.ImageName_ScanRegion_rep.isin(good_tissue_to_keep_limit)]
df = df[~df['ImageName_ScanRegion_rep'].isin(to_remove)]
In [655]:
fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL08A') & 
                                 (df['Condition'] == cond)]['otsu_1']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL08A')]['otsu_1'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[(df['Line'] == 'CTL08A')],
    y='otsu_1', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL04E') & 
                                 (df['Condition'] == cond)]['otsu_1']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL04E')]['otsu_1'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[df['Line'] == 'CTL04E'],
    y='otsu_1', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('KI67+ area (um2) / organoid area (um2)', fontsize=15)
ax[1].set_ylabel('KI67+ area (um2) / organoid area (um2)', fontsize=15)

plt.savefig('../../../../endpoints_figures/MKI67_density_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/MKI67_density_per_condition.png', dpi=300, bbox_inches='tight')
In [656]:
df[df.Condition.isin(['RET_AG', 'DMSO'])][['otsu_1', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[656]:
otsu_1
Condition
DMSO 0.032720
RET_AG 0.012457

Channel 0 - MAP2¶

In [657]:
mask_file_df_mix_2 = mask_file_df_mix_2.merge(sample_sheet, left_on='ImageName_ScanRegion', right_on='ImageName_ScanRegion', how='left')
In [658]:
#n_nuclei.reset_index(inplace=True)
#n_nuclei.rename(columns={'index': 'ImageName'}, inplace=True)
n_nuclei = {i.replace('_channel_2_measures', ''):j for i, j in zip(n_nuclei.keys(), n_nuclei.values())}
In [659]:
mask_file_df_mix_2['organoid_area_um2'] = mask_file_df_mix_2['ImageName_ScanRegion_Rep'].map({i:j for i, j in zip(organoid_areas.index, organoid_areas['area'])}) * (px_size ** 2)
mask_file_df_mix_2.set_index('ImageName_ScanRegion_Rep', inplace=True)
mask_file_df_mix_2['n_nuclei'] = n_nuclei
mask_file_df_mix_2['area_um2'] = mask_file_df_mix_2['area'] * (px_size ** 2)
In [660]:
mask_file_df_mix_2['pos_area_over_n_nuclei'] = mask_file_df_mix_2['area_um2'] / mask_file_df_mix_2['n_nuclei']
mask_file_df_mix_2['pos_area_over_tot'] = mask_file_df_mix_2['area_um2'] / mask_file_df_mix_2['organoid_area_um2']
In [661]:
mask_file_df_mix_2['Line'] = mask_file_df_mix_2['Condition_Line'].apply(lambda x: x.split('_')[-1] if pd.notnull(x) else x)
In [662]:
#mask_file_df_mix_2.to_csv("/group/testa/Project/EndPoints/TPSSU/analysis/measurements_mix_2_ch0_MAP2.csv")

Plot positive area/number of nuclei¶

In [663]:
df = mask_file_df_mix_2.loc[[i for i in good_tissue_to_keep_limit if i in mask_file_df_mix_2.index]]
fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL08A') & 
                                 (df['Condition'] == cond)]['pos_area_over_n_nuclei']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL08A')]['pos_area_over_n_nuclei'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[(df['Line'] == 'CTL08A')],
    y='pos_area_over_n_nuclei', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL04E') & 
                                 (df['Condition'] == cond)]['pos_area_over_n_nuclei']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL04E')]['pos_area_over_n_nuclei'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[df['Line'] == 'CTL04E'],
    y='pos_area_over_n_nuclei', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('MAP2+ area (um2) / # nuclei')
ax[1].set_ylabel('MAP2+ area (um2) / # nuclei')

plt.savefig('../../../../endpoints_figures/MAP2_pos_area_over_n_nuclei_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/MAP2_pos_area_over_n_nuclei_per_condition.png', dpi=300, bbox_inches='tight')
In [664]:
df[df.Condition.isin(['RET_AG', 'DMSO'])][['pos_area_over_n_nuclei', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[664]:
pos_area_over_n_nuclei
Condition
DMSO 66.925803
RET_AG 325.869393

Statistical testing for MAP2 area / number of nuclei¶

In [665]:
mask_file_df_mix_2_CTL04E = mask_file_df_mix_2[mask_file_df_mix_2['Line'] == 'CTL04E']
mask_file_df_mix_2_CTL04E['Condition'] = mask_file_df_mix_2_CTL04E['Condition'].astype('category')
mask_file_df_mix_2_CTL04E["log_area_over_n_nuclei"] = np.log(mask_file_df_mix_2_CTL04E["area_um2"] / mask_file_df_mix_2_CTL04E["n_nuclei"])
mask_file_df_mix_2_CTL04E['log_area_over_n_nuclei'].hist(bins = 10)

model_CTL04E = smf.ols("log_area_over_n_nuclei ~ C(Condition, Treatment(reference='DMSO'))",
                data=mask_file_df_mix_2_CTL04E,
                freq_weights=mask_file_df_mix_2_CTL04E["n_nuclei"]
                ).fit()

print(model_CTL04E.summary())
                              OLS Regression Results                              
==================================================================================
Dep. Variable:     log_area_over_n_nuclei   R-squared:                       0.437
Model:                                OLS   Adj. R-squared:                  0.081
Method:                     Least Squares   F-statistic:                     1.229
Date:                    Mon, 02 Mar 2026   Prob (F-statistic):              0.333
Time:                            15:08:59   Log-Likelihood:                -7.4707
No. Observations:                      32   AIC:                             40.94
Df Residuals:                          19   BIC:                             60.00
Df Model:                              12                                         
Covariance Type:                nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                 4.2615      0.280     15.196      0.000       3.675       4.849
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]      0.3638      0.397      0.917      0.371      -0.466       1.194
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]     0.2776      0.362      0.767      0.453      -0.480       1.035
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]     -0.0173      0.362     -0.048      0.962      -0.775       0.740
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]     0.2152      0.362      0.594      0.559      -0.543       0.973
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]      0.0009      0.486      0.002      0.999      -1.016       1.018
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]    -0.1143      0.362     -0.316      0.756      -0.872       0.643
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]      -0.4969      0.362     -1.372      0.186      -1.255       0.261
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]        0.4489      0.362      1.240      0.230      -0.309       1.207
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]      -0.2118      0.362     -0.585      0.565      -0.970       0.546
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]     -0.1283      0.397     -0.323      0.750      -0.958       0.702
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]      0.0087      0.362      0.024      0.981      -0.749       0.766
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]     0.4233      0.486      0.872      0.394      -0.593       1.440
==============================================================================
Omnibus:                       26.781   Durbin-Watson:                   2.471
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               85.509
Skew:                           1.559   Prob(JB):                     2.70e-19
Kurtosis:                      10.376   Cond. No.                         15.5
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
/tmp/ipykernel_3702104/64246595.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  mask_file_df_mix_2_CTL04E['Condition'] = mask_file_df_mix_2_CTL04E['Condition'].astype('category')
/tmp/ipykernel_3702104/64246595.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  mask_file_df_mix_2_CTL04E["log_area_over_n_nuclei"] = np.log(mask_file_df_mix_2_CTL04E["area_um2"] / mask_file_df_mix_2_CTL04E["n_nuclei"])
/group/testa/Users/alessia.valenti/NewImageAnalysis/.local/lib/python3.10/site-packages/statsmodels/base/model.py:130: ValueWarning: unknown kwargs ['freq_weights']
  warnings.warn(msg, ValueWarning)
In [666]:
mask_file_df_mix_2_CTL08A = mask_file_df_mix_2[mask_file_df_mix_2['Line'] == 'CTL08A']
mask_file_df_mix_2_CTL08A['Condition'] = mask_file_df_mix_2_CTL08A['Condition'].astype('category')
mask_file_df_mix_2_CTL08A["log_area_over_n_nuclei"] = np.log(mask_file_df_mix_2_CTL08A["area_um2"] / mask_file_df_mix_2_CTL08A["n_nuclei"])
mask_file_df_mix_2_CTL08A['log_area_over_n_nuclei'].hist(bins = 10)

model_CTL08A = smf.ols("log_area_over_n_nuclei ~ C(Condition, Treatment(reference='DMSO'))",
                data=mask_file_df_mix_2_CTL08A,
                freq_weights=mask_file_df_mix_2_CTL08A["n_nuclei"]
                ).fit()

print(model_CTL08A.summary())
/tmp/ipykernel_3702104/4202280779.py:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  mask_file_df_mix_2_CTL08A['Condition'] = mask_file_df_mix_2_CTL08A['Condition'].astype('category')
/tmp/ipykernel_3702104/4202280779.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  mask_file_df_mix_2_CTL08A["log_area_over_n_nuclei"] = np.log(mask_file_df_mix_2_CTL08A["area_um2"] / mask_file_df_mix_2_CTL08A["n_nuclei"])
                              OLS Regression Results                              
==================================================================================
Dep. Variable:     log_area_over_n_nuclei   R-squared:                       0.856
Model:                                OLS   Adj. R-squared:                  0.782
Method:                     Least Squares   F-statistic:                     11.48
Date:                    Mon, 02 Mar 2026   Prob (F-statistic):           5.90e-08
Time:                            15:09:00   Log-Likelihood:                 14.263
No. Observations:                      42   AIC:                             1.474
Df Residuals:                          27   BIC:                             27.54
Df Model:                              14                                         
Covariance Type:                nonrobust                                         
=========================================================================================================================
                                                            coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------------------------------------
Intercept                                                 4.2056      0.124     33.897      0.000       3.951       4.460
C(Condition, Treatment(reference='DMSO'))[T.ANDR_AG]      0.1453      0.175      0.828      0.415      -0.215       0.505
C(Condition, Treatment(reference='DMSO'))[T.ANDR_INH]     0.6053      0.175      3.450      0.002       0.245       0.965
C(Condition, Treatment(reference='DMSO'))[T.ARYL_AG]      0.2968      0.164      1.808      0.082      -0.040       0.634
C(Condition, Treatment(reference='DMSO'))[T.ARYL_INH]     0.3291      0.175      1.876      0.072      -0.031       0.689
C(Condition, Treatment(reference='DMSO'))[T.ESTR_AG]      0.5082      0.196      2.590      0.015       0.106       0.911
C(Condition, Treatment(reference='DMSO'))[T.ESTR_INH]     0.3283      0.196      1.674      0.106      -0.074       0.731
C(Condition, Treatment(reference='DMSO'))[T.GC_AG]       -0.0797      0.196     -0.406      0.688      -0.482       0.323
C(Condition, Treatment(reference='DMSO'))[T.GC_INH]       0.0141      0.175      0.080      0.937      -0.346       0.374
C(Condition, Treatment(reference='DMSO'))[T.LX_AG]        0.4626      0.175      2.636      0.014       0.103       0.823
C(Condition, Treatment(reference='DMSO'))[T.LX_INH]      -0.0925      0.196     -0.472      0.641      -0.495       0.310
C(Condition, Treatment(reference='DMSO'))[T.RET_AG]       1.4620      0.175      8.332      0.000       1.102       1.822
C(Condition, Treatment(reference='DMSO'))[T.RET_INH]     -0.0196      0.175     -0.112      0.912      -0.380       0.340
C(Condition, Treatment(reference='DMSO'))[T.THYR_AG]     -0.1537      0.175     -0.876      0.389      -0.514       0.206
C(Condition, Treatment(reference='DMSO'))[T.THYR_INH]    -0.2600      0.175     -1.482      0.150      -0.620       0.100
==============================================================================
Omnibus:                        4.498   Durbin-Watson:                   2.461
Prob(Omnibus):                  0.106   Jarque-Bera (JB):                3.653
Skew:                          -0.718   Prob(JB):                        0.161
Kurtosis:                       3.164   Cond. No.                         15.5
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
/group/testa/Users/alessia.valenti/NewImageAnalysis/.local/lib/python3.10/site-packages/statsmodels/base/model.py:130: ValueWarning: unknown kwargs ['freq_weights']
  warnings.warn(msg, ValueWarning)
In [667]:
resid_CTL08A = model_CTL08A.resid

stats.probplot(resid_CTL08A, dist="norm", plot=plt)
plt.show()
In [668]:
resid_CTL04E = model_CTL04E.resid

stats.probplot(resid_CTL04E, dist="norm", plot=plt)
plt.show()
In [669]:
pvals_CTL08A = model_CTL08A.pvalues[1:]  # exclude intercept
corrected_pvals_CTL08A = multipletests(pvals_CTL08A, method='fdr_bh')[1]

df_results_CTL08A = pd.concat([model_CTL08A.params, pd.Series(corrected_pvals_CTL08A, index=model_CTL08A.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL08A.columns = ['intercept', 'pvalue']
In [670]:
pvals_CTL04E = model_CTL04E.pvalues[1:]  # exclude intercept
corrected_pvals_CTL04E = multipletests(pvals_CTL04E, method='fdr_bh')[1]

df_results_CTL04E = pd.concat([model_CTL04E.params, pd.Series(corrected_pvals_CTL04E, index=model_CTL04E.params.index[1:])], axis = 1).iloc[1:]
df_results_CTL04E.columns = ['intercept', 'pvalue']
In [671]:
pvals = pd.concat([df_results_CTL08A['pvalue'], df_results_CTL04E['pvalue']], axis = 1)
pvals.columns = ['CTL08A', 'CTL04E']
pvals.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in pvals.index]
pvals
Out[671]:
CTL08A CTL04E
ANDR_AG 5.807400e-01 0.969314
ANDR_INH 1.301555e-02 0.969314
ARYL_AG 1.907124e-01 0.998520
ARYL_INH 1.907124e-01 0.969314
ESTR_AG 5.343187e-02 0.998520
ESTR_INH 2.114632e-01 0.998520
GC_AG 8.024340e-01 NaN
GC_INH 9.367019e-01 0.969314
LX_AG 5.343187e-02 0.969314
LX_INH 8.024340e-01 0.969314
RET_AG 8.529133e-08 NaN
RET_INH 9.367019e-01 0.998520
THYR_AG 5.807400e-01 0.998520
THYR_INH 2.623755e-01 0.969314
In [672]:
intercepts = pd.concat([df_results_CTL08A['intercept'], df_results_CTL04E['intercept']], axis = 1)
intercepts.columns = ['CTL08A', 'CTL04E']
intercepts.index = [i.split('C(Condition, Treatment(reference=\'DMSO\'))[T.')[1].replace(']', '') for i in intercepts.index]
intercepts = (np.exp(intercepts) - 1) * 100 # The percentage increase/decrease in MAP2+ area per nuclei compared to DMSO.
intercepts
Out[672]:
CTL08A CTL04E
ANDR_AG 15.640588 43.875257
ANDR_INH 83.171575 31.989519
ARYL_AG 34.550373 -1.713245
ARYL_INH 38.975719 24.011350
ESTR_AG 66.225582 0.091310
ESTR_INH 38.865425 -10.800735
GC_AG -7.659116 NaN
GC_INH 1.416405 -39.158859
LX_AG 58.813469 56.659567
LX_INH -8.836966 -19.086892
RET_AG 331.439273 NaN
RET_INH -1.939067 -12.040344
THYR_AG -14.248588 0.876280
THYR_INH -22.896690 52.702340
In [673]:
fig, ax = plt.subplots(figsize = (4,7))
intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")
intercepts = intercepts[pvals < 0.05].fillna('')
sns.heatmap(pvals, annot=intercepts, fmt='', cmap='Greens_r', cbar_kws={'label': 'p-value'}, vmin=0, vmax=0.05, ax = ax)
plt.title('Condition vs DMSO\n')
plt.savefig('../../../../endpoints_figures/endpoints_organoids_MAP2_n_nuclei_OLS_test.png', dpi=300, bbox_inches='tight')
/tmp/ipykernel_3702104/293039614.py:2: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead.
  intercepts = intercepts.applymap(lambda x: f"{x:+.1f}%" if pd.notnull(x) else "")

Plot positive area/total area¶

In [675]:
df = mask_file_df_mix_2.loc[[i for i in good_tissue_to_keep_limit if i in mask_file_df_mix_2.index]]
fig, ax = plt.subplots(1, 2, figsize=(20, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL08A') & 
                                 (df['Condition'] == cond)]['pos_area_over_tot']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL08A')]['pos_area_over_tot'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[(df['Line'] == 'CTL08A')],
    y='pos_area_over_tot', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL04E') & 
                                 (df['Condition'] == cond)]['pos_area_over_tot']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL04E')]['pos_area_over_tot'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[df['Line'] == 'CTL04E'],
    y='pos_area_over_tot', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('MAP2+ area (um2) / organoid area (um2)', fontsize=15)
ax[1].set_ylabel('MAP2+ area (um2) / organoid area (um2)', fontsize=15)

plt.savefig('../../../../endpoints_figures/MAP2_pos_area_over_tot_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/MAP2_pos_area_over_tot_per_condition.png', dpi=300, bbox_inches='tight')
In [676]:
df[df.Condition.isin(['RET_AG', 'DMSO'])][['pos_area_over_tot', 'Condition']].sort_values(by='Condition', ascending=False).groupby('Condition').median()
Out[676]:
pos_area_over_tot
Condition
DMSO 0.713576
RET_AG 0.745859

MKI67 / MAP2 ratio¶

In [677]:
n_nuclei_tot_pos_MKI67_otsu1 = {k: v['otsu_1'] for k, v in n_nuclei_tot_pos_MKI67.items() if 'otsu_1' in v}
In [678]:
n_nuclei_tot_pos_MKI67_otsu1
Out[678]:
{'20240424_manuel_lessi_0142_ScanRegion2_rep_0': 157,
 '20240424_manuel_lessi_0142_ScanRegion2_rep_2': 64,
 '20240424_manuel_lessi_0138_ScanRegion1_rep_2': 908,
 '20240424_manuel_lessi_0140_ScanRegion4_rep_1': 755,
 '20240424_manuel_lessi_0140_ScanRegion5_rep_2': 1063,
 '20240424_manuel_lessi_0139_ScanRegion3_rep_1': 1135,
 '20240424_manuel_lessi_0140_ScanRegion4_rep_2': 701,
 '20240424_manuel_lessi_0138_ScanRegion2_rep_0': 1373,
 '20240424_manuel_lessi_0143_ScanRegion0_rep_2': 499,
 '20240424_manuel_lessi_0139_ScanRegion4_rep_1': 693,
 '20240424_manuel_lessi_0139_ScanRegion4_rep_0': 509,
 '20240424_manuel_lessi_0137_ScanRegion4_rep_0': 607,
 '20240424_manuel_lessi_0140_ScanRegion1_rep_0': 210,
 '20240424_manuel_lessi_0137_ScanRegion0_rep_0': 688,
 '20240424_manuel_lessi_0140_ScanRegion5_rep_0': 376,
 '20240424_manuel_lessi_0138_ScanRegion2_rep_1': 1570,
 '20240424_manuel_lessi_0138_ScanRegion3_rep_2': 2686,
 '20240424_manuel_lessi_0137_ScanRegion5_rep_1': 760,
 '20240424_manuel_lessi_0139_ScanRegion3_rep_2': 812,
 '20240424_manuel_lessi_0137_ScanRegion2_rep_1': 108,
 '20240424_manuel_lessi_0138_ScanRegion4_rep_0': 756,
 '20240424_manuel_lessi_0137_ScanRegion1_rep_1': 892,
 '20240424_manuel_lessi_0139_ScanRegion2_rep_2': 22,
 '20240424_manuel_lessi_0137_ScanRegion4_rep_1': 1168,
 '20240424_manuel_lessi_0139_ScanRegion3_rep_0': 1014,
 '20240424_manuel_lessi_0138_ScanRegion4_rep_2': 107,
 '20240424_manuel_lessi_0137_ScanRegion3_rep_0': 159,
 '20240424_manuel_lessi_0143_ScanRegion1_rep_1': 985,
 '20240424_manuel_lessi_0137_ScanRegion5_rep_0': 1199,
 '20240424_manuel_lessi_0139_ScanRegion0_rep_0': 155,
 '20240424_manuel_lessi_0138_ScanRegion0_rep_2': 212,
 '20240424_manuel_lessi_0141_ScanRegion3_rep_0': 2127,
 '20240424_manuel_lessi_0143_ScanRegion0_rep_0': 388,
 '20240424_manuel_lessi_0138_ScanRegion3_rep_0': 1025,
 '20240424_manuel_lessi_0141_ScanRegion2_rep_1': 660,
 '20240424_manuel_lessi_0141_ScanRegion2_rep_2': 1279,
 '20240424_manuel_lessi_0139_ScanRegion1_rep_1': 1216,
 '20240424_manuel_lessi_0139_ScanRegion1_rep_0': 1086,
 '20240424_manuel_lessi_0140_ScanRegion3_rep_0': 618,
 '20240424_manuel_lessi_0140_ScanRegion1_rep_1': 1262,
 '20240424_manuel_lessi_0138_ScanRegion4_rep_1': 306,
 '20240424_manuel_lessi_0139_ScanRegion0_rep_1': 216,
 '20240424_manuel_lessi_0137_ScanRegion1_rep_0': 757,
 '20240424_manuel_lessi_0138_ScanRegion2_rep_2': 1845,
 '20240424_manuel_lessi_0138_ScanRegion5_rep_2': 634,
 '20240424_manuel_lessi_0140_ScanRegion0_rep_2': 1901,
 '20240424_manuel_lessi_0143_ScanRegion1_rep_0': 1273,
 '20240424_manuel_lessi_0139_ScanRegion0_rep_2': 617,
 '20240424_manuel_lessi_0138_ScanRegion0_rep_0': 1008,
 '20240424_manuel_lessi_0141_ScanRegion2_rep_0': 623,
 '20240424_manuel_lessi_0143_ScanRegion0_rep_1': 975,
 '20240424_manuel_lessi_0137_ScanRegion2_rep_0': 662,
 '20240424_manuel_lessi_0140_ScanRegion0_rep_1': 1066,
 '20240424_manuel_lessi_0138_ScanRegion0_rep_1': 29,
 '20240424_manuel_lessi_0140_ScanRegion1_rep_2': 336,
 '20240424_manuel_lessi_0137_ScanRegion3_rep_2': 270,
 '20240424_manuel_lessi_0138_ScanRegion1_rep_1': 651,
 '20240424_manuel_lessi_0140_ScanRegion3_rep_1': 257,
 '20240424_manuel_lessi_0137_ScanRegion1_rep_2': 845,
 '20240424_manuel_lessi_0137_ScanRegion3_rep_1': 321,
 '20240424_manuel_lessi_0140_ScanRegion4_rep_0': 899,
 '20240424_manuel_lessi_0140_ScanRegion0_rep_0': 2048,
 '20240424_manuel_lessi_0142_ScanRegion2_rep_1': 44,
 '20240424_manuel_lessi_0138_ScanRegion3_rep_3': 299,
 '20240424_manuel_lessi_0139_ScanRegion2_rep_1': 229,
 '20240424_manuel_lessi_0140_ScanRegion5_rep_1': 71,
 '20240424_manuel_lessi_0138_ScanRegion5_rep_0': 1362,
 '20240424_manuel_lessi_0139_ScanRegion2_rep_0': 584,
 '20240424_manuel_lessi_0140_ScanRegion3_rep_2': 551,
 '20240424_manuel_lessi_0141_ScanRegion3_rep_1': 342,
 '20240424_manuel_lessi_0138_ScanRegion5_rep_1': 845,
 '20240424_manuel_lessi_0138_ScanRegion3_rep_1': 840,
 '20240424_manuel_lessi_0138_ScanRegion1_rep_0': 999,
 '20240424_manuel_lessi_0140_ScanRegion2_rep_0': 248}
In [679]:
df['MKI67+'] = df.reset_index()['ImageName_ScanRegion_Rep'].map(n_nuclei_tot_pos_MKI67_otsu1).values
In [680]:
df["MKI67+_MAP2_ratio"] = df['MKI67+'] / df['area_um2']
In [681]:
fig, ax = plt.subplots(1, 2, figsize=(25, 5))
for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL08A') & 
                                 (df['Condition'] == cond)]['MKI67+_MAP2_ratio']
    box = ax[0].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
        # Add stripplot for CTL08A (single points per box)

    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL08A')]['MKI67+_MAP2_ratio'].median()
    ax[0].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[(df['Line'] == 'CTL08A')],
    y='MKI67+_MAP2_ratio', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[0], size=8
)

for i, cond in enumerate(hormonal_conditions):
    data = df[(df['Line'] == 'CTL04E') & 
                                 (df['Condition'] == cond)]['MKI67+_MAP2_ratio']
    box = ax[1].boxplot(data, positions=[i], patch_artist=True, widths=.8)
    for patch in box['boxes']:
        patch.set_facecolor(color_palette[cond])
        box['medians'][0].set_color('black')
    dmso_median = df[(df['Condition'] == 'DMSO') & 
                                       (df['Line'] == 'CTL04E')]['MKI67+_MAP2_ratio'].median()
    ax[1].axhline(dmso_median, color='red', linestyle='--', linewidth=.7, alpha=0.7)

sns.stripplot(
    data=df[df['Line'] == 'CTL04E'],
    y='MKI67+_MAP2_ratio', x='Condition', order=hormonal_conditions, color='black', alpha=0.5, jitter=False, ax=ax[1], size=8
)

_ = ax[0].set_xticks(range(len(hormonal_conditions)))
_ = ax[0].set_xticklabels(hormonal_conditions, rotation=90)

_ = ax[1].set_xticks(range(len(hormonal_conditions)))
_ = ax[1].set_xticklabels(hormonal_conditions, rotation=90)

ax[0].set_title('CTL08A')
ax[1].set_title('CTL04E')

ax[0].set_ylabel('MKI67+/MAP2 ratio (nuclei/um2)')
ax[1].set_ylabel('MKI67+/MAP2 ratio (nuclei/um2)')

plt.savefig('../../../../endpoints_figures/MKI67+_MAP2_ratio_per_condition.pdf', dpi=300, bbox_inches='tight')
plt.savefig('../../../../endpoints_figures/MKI67+_MAP2_ratio_per_condition.png', dpi=300, bbox_inches='tight')