pc-123 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pc_123-0.1.0/LICENSE ADDED
File without changes
pc_123-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: pc-123
3
+ Version: 0.1.0
4
+ Summary: Lab experiment code viewer
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Dynamic: license-file
pc_123-0.1.0/README.md ADDED
File without changes
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pc-123"
7
+ version = "0.1.0"
8
+ description = "Lab experiment code viewer"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+
12
+ [project.scripts]
13
+ pc-123 = "pc123.cli:main"
14
+
15
+ [tool.setuptools.packages.find]
16
+ where = ["src"]
17
+
18
+ [tool.setuptools.package-data]
19
+ "pc123.experiments" = ["*.py"]
pc_123-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,23 @@
1
+ import sys
2
+ from importlib import resources
3
+
4
+ def main():
5
+ if len(sys.argv) < 2:
6
+ print("Usage: pc-123 <experiment_number>")
7
+ return
8
+
9
+ num = sys.argv[1]
10
+ try:
11
+ code = resources.files("pc123.experiments").joinpath(f"exp{num}.py").read_text(encoding="utf-8")
12
+ except FileNotFoundError:
13
+ print(f"Experiment {num} not found.")
14
+ return
15
+
16
+ print("=" * 60)
17
+ print(f"# EXP{num}")
18
+ print("=" * 60)
19
+ print()
20
+ print(code)
21
+
22
+ if __name__ == "__main__":
23
+ main()
@@ -0,0 +1,21 @@
1
+ # Step 1: Import libraries
2
+ import pandas as pd
3
+ import numpy as np
4
+
5
+ # Step 2: Load dataset
6
+ df = pd.read_csv('clinical_data.csv')
7
+
8
+ # Step 3: Display basic information
9
+ print("Dataset Shape:", df.shape)
10
+ print("\nFirst 5 Records:\n", df.head())
11
+ print("\nColumn Names:\n", df.columns.tolist())
12
+ print("\nData Types:\n", df.dtypes)
13
+
14
+ # Step 4: Generate summary statistics
15
+ print("\nDescriptive Statistics:\n", df.describe())
16
+
17
+ # Step 5: Check missing values
18
+ print("\nMissing Values:\n", df.isnull().sum())
19
+
20
+ print("\nDataset Information:")
21
+ df.info()
@@ -0,0 +1,25 @@
1
+ # Step 1: Import libraries
2
+ import pandas as pd
3
+ import numpy as np
4
+
5
+ # Step 2: Load dataset
6
+ df = pd.read_csv('clinical_data.csv')
7
+
8
+ # Step 3: Check missing values
9
+ print("Missing Values:\n", df.isnull().sum())
10
+
11
+ # Step 4: Handle missing values
12
+ df_clean = df.dropna()
13
+ df_clean = df.fillna(df.mean(numeric_only=True))
14
+
15
+ # Step 5: Check and remove duplicate rows
16
+ print("Duplicate Rows:", df_clean.duplicated().sum())
17
+ df_clean.drop_duplicates(inplace=True)
18
+
19
+ # Step 6: Standardize inconsistent text entries
20
+ df_clean['gender'] = df_clean['gender'].str.strip().str.lower()
21
+
22
+ # Step 7: Verify cleaned dataset
23
+ print("\nMissing Values After Cleaning:\n", df_clean.isnull().sum())
24
+ print("Duplicate Rows After Cleaning:", df_clean.duplicated().sum())
25
+ df_clean.info()
@@ -0,0 +1,39 @@
1
+ # Step 1: Import libraries
2
+ import pandas as pd
3
+ import numpy as np
4
+
5
+ # Step 2: Load datasets
6
+ demo = pd.read_csv('demographics.csv')
7
+ vitals = pd.read_csv('vitals.csv')
8
+ print(demo.shape, vitals.shape)
9
+
10
+ # Step 3: Check the key variable for duplicates
11
+ print("Duplicate IDs (demo):", demo['patient_id'].duplicated().sum())
12
+ print("Duplicate IDs (vitals):", vitals['patient_id'].duplicated().sum())
13
+
14
+ # Step 4: Merge datasets on patient_id
15
+ merged = pd.merge(demo, vitals, on='patient_id', how='inner') # how='left' keeps all patients from demo
16
+ print("Merged shape:", merged.shape)
17
+
18
+ # Step 5: Create derived variables
19
+ merged['bmi'] = (merged['weight_kg'] / (merged['height_cm'] / 100) ** 2).round(1)
20
+
21
+ merged['age_group'] = pd.cut(merged['age'],
22
+ bins=[0, 17, 39, 59, 120],
23
+ labels=['<18', '18-39', '40-59', '60+'])
24
+
25
+ merged['bmi_category'] = np.select(
26
+ [merged['bmi'] < 18.5, merged['bmi'] < 25, merged['bmi'] < 30],
27
+ ['Underweight', 'Normal', 'Overweight'],
28
+ default='Obese')
29
+
30
+ merged['hypertension_flag'] = np.where(merged['sbp_baseline'] >= 140, 1, 0)
31
+ merged['sbp_change'] = merged['sbp_final'] - merged['sbp_baseline']
32
+
33
+ # Step 6: Verify the transformed dataset
34
+ print(merged[['patient_id', 'bmi', 'age_group', 'bmi_category',
35
+ 'hypertension_flag', 'sbp_change']].head())
36
+ merged.info()
37
+
38
+ # Step 7: Save the transformed dataset
39
+ merged.to_csv('clinical_transformed.csv', index=False)
@@ -0,0 +1,47 @@
1
+ # Step 1: Import libraries
2
+ import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+
5
+ # Step 2: Load the transformed dataset
6
+ df = pd.read_csv('clinical_transformed.csv')
7
+
8
+ # Step 3: Bar chart - number of patients in each BMI category
9
+ counts = df['bmi_category'].value_counts()
10
+ plt.figure(figsize=(6, 4))
11
+ plt.bar(counts.index, counts.values, color='steelblue')
12
+ plt.title('Patients by BMI Category')
13
+ plt.xlabel('BMI Category')
14
+ plt.ylabel('Number of Patients')
15
+ plt.tight_layout()
16
+ plt.savefig('bar_bmi_category.png')
17
+ plt.show()
18
+
19
+ # Step 4: Histogram - distribution of age
20
+ plt.figure(figsize=(6, 4))
21
+ plt.hist(df['age'], bins=10, color='skyblue', edgecolor='black')
22
+ plt.title('Age Distribution of Patients')
23
+ plt.xlabel('Age (years)')
24
+ plt.ylabel('Frequency')
25
+ plt.tight_layout()
26
+ plt.savefig('hist_age.png')
27
+ plt.show()
28
+
29
+ # Step 5: Box plot - baseline systolic BP by gender
30
+ df.boxplot(column='sbp_baseline', by='gender', figsize=(6, 4))
31
+ plt.title('Baseline Systolic BP by Gender')
32
+ plt.suptitle('')
33
+ plt.xlabel('Gender')
34
+ plt.ylabel('Systolic BP (mmHg)')
35
+ plt.tight_layout()
36
+ plt.savefig('box_sbp_gender.png')
37
+ plt.show()
38
+
39
+ # Step 6: Compare patient groups - mean change in systolic BP by gender
40
+ print(df.groupby('gender')['sbp_change'].describe())
41
+
42
+ df.groupby('gender')['sbp_change'].mean().plot(kind='bar', color='steelblue')
43
+ plt.title('Mean Change in Systolic BP by Gender')
44
+ plt.xlabel('Gender')
45
+ plt.ylabel('Mean Change (mmHg)')
46
+ plt.tight_layout()
47
+ plt.show()
@@ -0,0 +1,54 @@
1
+ import pandas as pd
2
+
3
+ # Step 1: Load raw datasets
4
+ raw_demo = pd.read_csv('demographics.csv')
5
+ raw_vitals = pd.read_csv('vitals.csv') # columns: patient_id, visit, sbp
6
+
7
+ STUDY = 'STUDY01'
8
+
9
+ # Step 2: Create DM (Demographics) domain - one row per subject
10
+ dm = pd.DataFrame({
11
+ 'STUDYID': STUDY,
12
+ 'DOMAIN': 'DM',
13
+ 'USUBJID': STUDY + '-' + raw_demo['patient_id'].astype(str),
14
+ 'SUBJID': raw_demo['patient_id'].astype(str),
15
+ 'AGE': raw_demo['age'],
16
+ 'AGEU': 'YEARS',
17
+ 'SEX': raw_demo['gender'].str.strip().str.upper().str[0]
18
+ })
19
+
20
+ # Step 3: Create VS (Vital Signs) domain - one record per test per visit
21
+ vs = pd.DataFrame({
22
+ 'STUDYID': STUDY,
23
+ 'DOMAIN': 'VS',
24
+ 'USUBJID': STUDY + '-' + raw_vitals['patient_id'].astype(str),
25
+ 'VSTESTCD': 'SYSBP',
26
+ 'VSTEST': 'Systolic Blood Pressure',
27
+ 'VSSTRESN': raw_vitals['sbp'],
28
+ 'VSSTRESU': 'mmHg',
29
+ 'VISIT': raw_vitals['visit'].str.strip().str.upper()
30
+ })
31
+
32
+ # Visit order: add every visit name that appears in your data
33
+ visit_order = {'SCREENING': 0, 'BASELINE': 1, 'WEEK 4': 2, 'WEEK 8': 3, 'WEEK 12': 4}
34
+ vs['VISITNUM'] = vs['VISIT'].map(visit_order)
35
+ vs['VSBLFL'] = vs['VISIT'].eq('BASELINE').map({True: 'Y', False: ''})
36
+
37
+ vs = vs.sort_values(['USUBJID', 'VISITNUM']).reset_index(drop=True)
38
+ vs['VSSEQ'] = vs.groupby('USUBJID').cumcount() + 1
39
+
40
+ # Step 4: Variable labels
41
+ dm.attrs['labels'] = {
42
+ 'STUDYID': 'Study Identifier', 'DOMAIN': 'Domain Abbreviation',
43
+ 'USUBJID': 'Unique Subject Identifier', 'SUBJID': 'Subject Identifier for the Study',
44
+ 'AGE': 'Age', 'AGEU': 'Age Units', 'SEX': 'Sex'}
45
+ vs.attrs['labels'] = {
46
+ 'STUDYID': 'Study Identifier', 'DOMAIN': 'Domain Abbreviation',
47
+ 'USUBJID': 'Unique Subject Identifier', 'VSSEQ': 'Sequence Number',
48
+ 'VSTESTCD': 'Vital Signs Test Short Name', 'VSTEST': 'Vital Signs Test Name',
49
+ 'VSSTRESN': 'Numeric Result/Finding in Standard Units', 'VSSTRESU': 'Standard Units',
50
+ 'VSBLFL': 'Baseline Flag', 'VISITNUM': 'Visit Number', 'VISIT': 'Visit Name'}
51
+
52
+ # Step 5: Save
53
+ dm.to_csv('dm.csv', index=False)
54
+ vs.to_csv('vs.csv', index=False)
@@ -0,0 +1,45 @@
1
+ # Step 1: Import libraries
2
+ import pandas as pd
3
+ import numpy as np
4
+
5
+ # Step 2: Load SDTM datasets (from Experiment 5)
6
+ dm = pd.read_csv('dm.csv')
7
+ vs = pd.read_csv('vs.csv')
8
+
9
+ # Step 3: Create ADSL (Subject-Level Analysis Dataset)
10
+ adsl = dm.drop(columns='DOMAIN')
11
+ adsl['AGEGR1'] = pd.cut(adsl['AGE'],
12
+ bins=[0, 39, 59, 200],
13
+ labels=['<40', '40-59', '>=60']).astype(str)
14
+ adsl['SAFFL'] = 'Y'
15
+
16
+ # Step 4: Create ADVS (Vital Signs Analysis Dataset)
17
+ advs = vs.merge(adsl[['USUBJID', 'AGE', 'SEX', 'AGEGR1', 'SAFFL']],
18
+ on='USUBJID', how='left')
19
+ advs['PARAMCD'] = advs['VSTESTCD']
20
+ advs['PARAM'] = advs['VSTEST'] + ' (' + advs['VSSTRESU'] + ')'
21
+ advs['AVAL'] = advs['VSSTRESN']
22
+ advs['AVISIT'] = advs['VISIT']
23
+ advs['AVISITN'] = advs['VISIT'].map({'BASELINE': 0, 'WEEK 12': 12})
24
+ advs['ABLFL'] = np.where(advs['VISIT'] == 'BASELINE', 'Y', '')
25
+
26
+ # Step 5: Derive baseline, change from baseline and percent change
27
+ base = advs.loc[advs['ABLFL'] == 'Y', ['USUBJID', 'PARAMCD', 'AVAL']]
28
+ base = base.rename(columns={'AVAL': 'BASE'})
29
+ advs = advs.merge(base, on=['USUBJID', 'PARAMCD'], how='left')
30
+ advs['CHG'] = advs['AVAL'] - advs['BASE']
31
+ advs['PCHG'] = (advs['CHG'] / advs['BASE'] * 100).round(1)
32
+ advs.loc[advs['ABLFL'] == 'Y', ['CHG', 'PCHG']] = np.nan # post-baseline only
33
+
34
+ # Step 6: Select variables and verify
35
+ advs = advs[['STUDYID', 'USUBJID', 'AGE', 'SEX', 'AGEGR1', 'SAFFL',
36
+ 'PARAMCD', 'PARAM', 'AVISIT', 'AVISITN',
37
+ 'AVAL', 'ABLFL', 'BASE', 'CHG', 'PCHG']]
38
+ advs = advs.sort_values(['USUBJID', 'AVISITN']).reset_index(drop=True)
39
+ print(adsl.head())
40
+ print(advs.head())
41
+ advs.info()
42
+
43
+ # Step 7: Save the ADaM datasets
44
+ adsl.to_csv('adsl.csv', index=False)
45
+ advs.to_csv('advs.csv', index=False)
@@ -0,0 +1,34 @@
1
+ # Step 1: Import libraries
2
+ import pandas as pd
3
+ from scipy import stats
4
+ from statsmodels.stats.multicomp import pairwise_tukeyhsd
5
+
6
+ # Step 2: Load the ADaM dataset (from Experiment 6)
7
+ advs = pd.read_csv('advs.csv')
8
+ post = advs[advs['AVISIT'] == 'WEEK 12'].dropna(subset=['CHG']).copy()
9
+
10
+ # Step 3: Descriptive statistics by group
11
+ print(post.groupby('SEX')['CHG'].describe())
12
+ print(post.groupby('AGEGR1')['CHG'].describe())
13
+
14
+ # Step 4: Paired t-test - baseline vs. Week 12 systolic BP
15
+ t_stat, p_val = stats.ttest_rel(post['BASE'], post['AVAL'])
16
+ print("Paired t-test: t =", round(t_stat, 3), ", p =", round(p_val, 4))
17
+
18
+ # Step 5: Independent t-test - change from baseline, male vs. female
19
+ male = post.loc[post['SEX'] == 'M', 'CHG']
20
+ female = post.loc[post['SEX'] == 'F', 'CHG']
21
+ t_stat, p_val = stats.ttest_ind(male, female, equal_var=False)
22
+ print("Independent t-test: t =", round(t_stat, 3), ", p =", round(p_val, 4))
23
+
24
+ # Step 6: One-way ANOVA - change from baseline across age groups
25
+ groups = [g['CHG'].values for _, g in post.groupby('AGEGR1')]
26
+ f_stat, p_val = stats.f_oneway(*groups)
27
+ print("ANOVA: F =", round(f_stat, 3), ", p =", round(p_val, 4))
28
+
29
+ # Step 7: Post-hoc test (Tukey HSD) between age groups
30
+ print(pairwise_tukeyhsd(post['CHG'], post['AGEGR1']))
31
+
32
+ # Step 8: Interpret the results
33
+ alpha = 0.05
34
+ print("Significant at 5% level" if p_val < alpha else "Not significant at 5% level")
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: pc-123
3
+ Version: 0.1.0
4
+ Summary: Lab experiment code viewer
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Dynamic: license-file
@@ -0,0 +1,17 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/pc123/__init__.py
5
+ src/pc123/cli.py
6
+ src/pc123/experiments/exp1.py
7
+ src/pc123/experiments/exp2.py
8
+ src/pc123/experiments/exp3.py
9
+ src/pc123/experiments/exp4.py
10
+ src/pc123/experiments/exp5.py
11
+ src/pc123/experiments/exp6.py
12
+ src/pc123/experiments/exp7.py
13
+ src/pc_123.egg-info/PKG-INFO
14
+ src/pc_123.egg-info/SOURCES.txt
15
+ src/pc_123.egg-info/dependency_links.txt
16
+ src/pc_123.egg-info/entry_points.txt
17
+ src/pc_123.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pc-123 = pc123.cli:main
@@ -0,0 +1 @@
1
+ pc123