meta-edc 1.0.7__py3-none-any.whl → 1.1.1__py3-none-any.whl
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.
- meta_ae/action_items.py +10 -2
- meta_ae/baker_recipes.py +1 -2
- meta_ae/tests/tests/test_actions.py +1 -2
- meta_analytics/README.rst +1 -2
- meta_analytics/notebooks/anu.ipynb +95 -0
- meta_analytics/notebooks/appointment_planning.ipynb +329 -0
- meta_analytics/notebooks/arvs.ipynb +103 -0
- meta_analytics/notebooks/cleaning/consent_v1_ext.ipynb +227 -0
- meta_analytics/notebooks/cleaning/offschedule_eos.ipynb +353 -0
- meta_analytics/notebooks/dsmc/renal_dysfunction.ipynb +435 -0
- meta_analytics/notebooks/endpoints/meta_endpoints_by_date.ipynb +664 -0
- meta_analytics/notebooks/followup_examination.ipynb +141 -0
- meta_analytics/notebooks/hba1c.ipynb +136 -0
- meta_analytics/notebooks/hiv_regimens.ipynb +122 -118
- meta_analytics/notebooks/incidence.ipynb +232 -0
- meta_analytics/notebooks/liver.ipynb +389 -0
- meta_analytics/notebooks/magreth.ipynb +645 -0
- meta_analytics/notebooks/monitoring_report.ipynb +721 -448
- meta_analytics/notebooks/pharmacy.ipynb +405 -306
- meta_analytics/notebooks/pharmacy_stock_202410.ipynb +306 -0
- meta_analytics/notebooks/steering.ipynb +61 -0
- meta_analytics/notebooks/undiagnosed/meta3_screening_consort_chart.ipynb +1176 -0
- meta_analytics/notebooks/undiagnosed/meta3_screening_undiagnosed.ipynb +519 -0
- meta_analytics/notebooks/undiagnosed/meta_screening_table2.ipynb +964 -0
- meta_analytics/notebooks/undiagnosed/screen_undiagnosed_or.ipynb +296 -0
- meta_analytics/notebooks/undiagnosed/screening.ipynb +273 -0
- meta_analytics/notebooks/undiagnosed/screening2.ipynb +958 -0
- meta_analytics/notebooks/undiagnosed/screening_undiagnosed_20241002.ipynb +958 -0
- meta_analytics/notebooks/ven.ipynb +191 -0
- meta_analytics/notebooks/vitals.ipynb +263 -0
- meta_edc/settings/debug.py +3 -2
- meta_edc/urls.py +1 -0
- {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/METADATA +3 -3
- {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/RECORD +62 -35
- {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/WHEEL +1 -1
- meta_labs/reportables.py +14 -11
- meta_labs/tests/test_reportables.py +33 -12
- meta_pharmacy/notebooks/pharmacy.ipynb +41 -0
- meta_prn/admin/offschedule_pregnancy_admin.py +3 -3
- meta_prn/admin/onschedule_dm_referral_admin.py +5 -5
- meta_prn/form_validators/end_of_study.py +2 -2
- meta_prn/migrations/0063_historicaloffstudymedication_singleton_field_and_more.py +37 -0
- meta_prn/migrations/0064_auto_20250602_2143.py +18 -0
- meta_prn/models/end_of_study.py +2 -0
- meta_prn/models/off_study_medication.py +2 -0
- meta_reports/admin/last_imp_refill_admin.py +3 -2
- meta_screening/eligibility/eligibility_part_three/base_eligibility_part_three.py +59 -47
- meta_screening/form_validators/screening_part_three.py +6 -1
- meta_screening/tests/meta_test_case_mixin.py +3 -0
- meta_screening/tests/tests/test_forms.py +9 -2
- meta_screening/tests/tests/test_screening_part_three.py +11 -14
- meta_subject/action_items.py +2 -3
- meta_subject/choices.py +2 -1
- meta_subject/form_validators/delivery_form_validator.py +1 -0
- meta_subject/forms/blood_results/blood_results_rft_form.py +60 -3
- meta_subject/forms/delivery_form.py +2 -0
- meta_subject/migrations/0223_bloodresultsfbc_errors_bloodresultsgludummy_errors_and_more.py +83 -0
- meta_subject/migrations/0224_bloodresultsfbc_abnormal_summary_and_more.py +153 -0
- meta_subject/tests/tests/test_egfr.py +5 -5
- meta_analytics/dataframes/enrolled/__init__.py +0 -0
- {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/licenses/LICENSE +0 -0
- {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/top_level.txt +0 -0
@@ -2,12 +2,15 @@
|
|
2
2
|
"cells": [
|
3
3
|
{
|
4
4
|
"cell_type": "code",
|
5
|
-
"
|
5
|
+
"execution_count": null,
|
6
|
+
"id": "0",
|
6
7
|
"metadata": {},
|
8
|
+
"outputs": [],
|
7
9
|
"source": [
|
8
10
|
"%%capture\n",
|
9
11
|
"import os\n",
|
10
12
|
"from pathlib import Path\n",
|
13
|
+
"\n",
|
11
14
|
"import pandas as pd\n",
|
12
15
|
"from dj_notebook import activate\n",
|
13
16
|
"import numpy as np\n",
|
@@ -19,17 +22,17 @@
|
|
19
22
|
"pharmacy_folder = Path(os.environ[\"META_PHARMACY_FOLDER\"])\n",
|
20
23
|
"plus = activate(dotenv_file=env_file)\n",
|
21
24
|
"pd.set_option('future.no_silent_downcasting', True)"
|
22
|
-
]
|
23
|
-
"outputs": [],
|
24
|
-
"execution_count": null
|
25
|
+
]
|
25
26
|
},
|
26
27
|
{
|
27
28
|
"cell_type": "code",
|
28
|
-
"
|
29
|
+
"execution_count": null,
|
30
|
+
"id": "1",
|
29
31
|
"metadata": {},
|
32
|
+
"outputs": [],
|
30
33
|
"source": [
|
31
|
-
"\n",
|
32
34
|
"import pdfkit\n",
|
35
|
+
"from typing import Callable\n",
|
33
36
|
"from datetime import date\n",
|
34
37
|
"from edc_pdutils.dataframes import get_subject_visit\n",
|
35
38
|
"from meta_visit_schedule.constants import MONTH15, MONTH18, MONTH21, MONTH27, MONTH30, MONTH33, MONTH39\n",
|
@@ -43,37 +46,44 @@
|
|
43
46
|
"from edc_appointment.analytics import get_appointment_df\n",
|
44
47
|
"from edc_appointment.constants import NEW_APPT, CANCELLED_APPT, ONTIME_APPT, MISSED_APPT\n",
|
45
48
|
"from meta_consent.models import SubjectConsentV1Ext\n",
|
46
|
-
"from meta_analytics.dataframes import get_glucose_df\n",
|
49
|
+
"from meta_analytics.dataframes import get_glucose_df, get_screening_df\n",
|
47
50
|
"\n",
|
48
|
-
"from edc_appointment.constants import SCHEDULED_APPT, UNSCHEDULED_APPT
|
49
|
-
"from edc_constants.constants import YES
|
50
|
-
]
|
51
|
-
"outputs": [],
|
52
|
-
"execution_count": null
|
51
|
+
"from edc_appointment.constants import SCHEDULED_APPT, UNSCHEDULED_APPT # noqa\n",
|
52
|
+
"from edc_constants.constants import YES # noqa"
|
53
|
+
]
|
53
54
|
},
|
54
55
|
{
|
55
|
-
"metadata": {},
|
56
56
|
"cell_type": "code",
|
57
|
+
"execution_count": null,
|
58
|
+
"id": "2",
|
59
|
+
"metadata": {},
|
60
|
+
"outputs": [],
|
57
61
|
"source": [
|
58
62
|
"html_data = []\n",
|
59
|
-
"
|
60
|
-
"
|
61
|
-
"
|
63
|
+
"data_download_date = date(2025, 6, 30)\n",
|
64
|
+
"cutoff_date = date(2025, 6, 30)\n",
|
65
|
+
"end_of_trial_date = date(2026, 7, 1)\n",
|
66
|
+
"document_title = f\"<h2>Monitoring Report: {cutoff_date.strftime('%B %Y')}</h2><h5>Data Download: {data_download_date.strftime('%d %B %Y')}</h5>\"\n",
|
62
67
|
"study_title = 'META3 - Metformin treatment for diabetes prevention in Africa'\n",
|
63
68
|
"pdf_filename = f\"monitoring_report_{cutoff_date.strftime('%Y%m%d')}.pdf\"\n"
|
64
|
-
]
|
65
|
-
"id": "b255fd34cd6f50c0",
|
66
|
-
"outputs": [],
|
67
|
-
"execution_count": null
|
69
|
+
]
|
68
70
|
},
|
69
71
|
{
|
70
|
-
"metadata": {},
|
71
72
|
"cell_type": "code",
|
73
|
+
"execution_count": null,
|
74
|
+
"id": "3",
|
75
|
+
"metadata": {},
|
76
|
+
"outputs": [],
|
72
77
|
"source": [
|
78
|
+
"# 105-30-0288-5 should also be late excluded based on the haemoglobin 4.8 presented at baseline\n",
|
73
79
|
"\n",
|
74
80
|
"df_visit = get_subject_visit(\"meta_subject.subjectvisit\")\n",
|
75
|
-
"
|
81
|
+
"df_visit_1691 = df_visit.copy()\n",
|
82
|
+
"\n",
|
83
|
+
"late_exlusion_offstudy_reasons = [\n",
|
84
|
+
" 'Patient fulfilled late exclusion criteria (due to abnormal blood values or raised blood pressure at enrolment']\n",
|
76
85
|
"df_eos = get_eos_df()\n",
|
86
|
+
"df_eos_1691 = df_eos.copy()\n",
|
77
87
|
"df_eos_excluded = (\n",
|
78
88
|
" df_eos\n",
|
79
89
|
" .query(\"offstudy_reason.isin(@late_exlusion_offstudy_reasons)\")\n",
|
@@ -82,23 +92,25 @@
|
|
82
92
|
")\n",
|
83
93
|
"df_visit = (\n",
|
84
94
|
" df_visit\n",
|
85
|
-
" .merge(df_eos_excluded[[\"subject_identifier\", \"offstudy_datetime\", \"offstudy_reason\"]], on=\"subject_identifier\"
|
95
|
+
" .merge(df_eos_excluded[[\"subject_identifier\", \"offstudy_datetime\", \"offstudy_reason\"]], on=\"subject_identifier\",\n",
|
96
|
+
" how=\"left\", indicator=True)\n",
|
86
97
|
" .query(\"_merge=='left_only'\")\n",
|
87
98
|
" .drop(columns=[\"_merge\"])\n",
|
88
99
|
")\n",
|
89
100
|
"\n",
|
90
|
-
"df_visit = df_visit[df_visit.appt_datetime.dt.date<=cutoff_date]\n",
|
101
|
+
"df_visit = df_visit[df_visit.appt_datetime.dt.date <= cutoff_date]\n",
|
91
102
|
"\n",
|
92
103
|
"df_appointments = get_appointment_df()\n",
|
93
104
|
"df_appointments[\"site_id\"] = df_appointments.site_id.astype(str)\n",
|
105
|
+
"df_appointments_1691 = df_appointments.copy()\n",
|
94
106
|
"df_appointments = (\n",
|
95
107
|
" df_appointments\n",
|
96
|
-
" .merge(df_eos_excluded[[\"subject_identifier\", \"offstudy_datetime\", \"offstudy_reason\"]], on=\"subject_identifier\"
|
108
|
+
" .merge(df_eos_excluded[[\"subject_identifier\", \"offstudy_datetime\", \"offstudy_reason\"]], on=\"subject_identifier\",\n",
|
109
|
+
" how=\"left\", indicator=True)\n",
|
97
110
|
" .query(\"_merge=='left_only'\")\n",
|
98
111
|
" .drop(columns=[\"_merge\"])\n",
|
99
112
|
")\n",
|
100
113
|
"\n",
|
101
|
-
"\n",
|
102
114
|
"cls = GlucoseEndpointsByDate()\n",
|
103
115
|
"cls.run()\n",
|
104
116
|
"df_endpoint = cls.endpoint_only_df.copy()\n",
|
@@ -116,32 +128,61 @@
|
|
116
128
|
" .reset_index()\n",
|
117
129
|
" .pivot_table(columns=\"site_id\", values=0, observed=True)\n",
|
118
130
|
")\n",
|
119
|
-
"enrolled_pivot.columns.name
|
120
|
-
"enrolled_pivot[\"total\"] = enrolled_pivot[[\"10\", \"20\"
|
121
|
-
|
122
|
-
],
|
123
|
-
"id": "215212f9d44e79df",
|
124
|
-
"outputs": [],
|
125
|
-
"execution_count": null
|
131
|
+
"enrolled_pivot.columns.name = \"\"\n",
|
132
|
+
"enrolled_pivot[\"total\"] = enrolled_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\"]].sum(axis=1)"
|
133
|
+
]
|
126
134
|
},
|
127
135
|
{
|
128
|
-
"metadata": {},
|
129
136
|
"cell_type": "code",
|
130
|
-
"
|
131
|
-
|
132
|
-
|
133
|
-
],
|
134
|
-
"id": "fe90271ff1799692",
|
137
|
+
"execution_count": null,
|
138
|
+
"id": "4",
|
139
|
+
"metadata": {},
|
135
140
|
"outputs": [],
|
136
|
-
"
|
141
|
+
"source": [
|
142
|
+
"# before late exclusion\n",
|
143
|
+
"df_visit_orig = df_visit_1691[df_visit_1691.appt_datetime.dt.date <= cutoff_date]\n",
|
144
|
+
"enrolled_1691 = df_visit_1691.copy()\n",
|
145
|
+
"enrolled_1691[\"site_id\"] = enrolled_1691[\"site_id\"].astype(str)\n",
|
146
|
+
"enrolled_1691_pivot = (\n",
|
147
|
+
" enrolled_1691\n",
|
148
|
+
" .query(\"visit_code==1000.0\").groupby([\"site_id\"])\n",
|
149
|
+
" .size()\n",
|
150
|
+
" .reset_index()\n",
|
151
|
+
" .pivot_table(columns=\"site_id\", values=0, observed=True)\n",
|
152
|
+
")\n",
|
153
|
+
"enrolled_1691_pivot.columns.name = \"\"\n",
|
154
|
+
"enrolled_1691_pivot[\"total\"] = enrolled_1691_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\"]].sum(axis=1)\n",
|
155
|
+
"\n",
|
156
|
+
"# df_eos_1691\n",
|
157
|
+
"# df_appointments_1691\n",
|
158
|
+
"\n"
|
159
|
+
]
|
137
160
|
},
|
138
161
|
{
|
162
|
+
"cell_type": "code",
|
163
|
+
"execution_count": null,
|
164
|
+
"id": "5",
|
139
165
|
"metadata": {},
|
166
|
+
"outputs": [],
|
167
|
+
"source": [
|
168
|
+
"column_headers = {\"label\": \"Label\", \"visit_code\": \"Visit code\", \"10\": \"Hindu Mandal\", \"20\": \"Amana\", \"30\": \"Temeke\",\n",
|
169
|
+
" \"40\": \"Mwananyamala\", \"60\": \"Mnazi Moja\", \"total\": \"Total\"}\n",
|
170
|
+
"column_headers_with_str = {\"label\": \"Label\", \"10_str\": \"Hindu Mandal\", \"20_str\": \"Amana\", \"30_str\": \"Temeke\",\n",
|
171
|
+
" \"40_str\": \"Mwananyamala\", \"60_str\": \"Mnazi Moja\", \"total_str\": \"Total\"}"
|
172
|
+
]
|
173
|
+
},
|
174
|
+
{
|
140
175
|
"cell_type": "code",
|
176
|
+
"execution_count": null,
|
177
|
+
"id": "6",
|
178
|
+
"metadata": {},
|
179
|
+
"outputs": [],
|
141
180
|
"source": [
|
142
181
|
"# Table 1a Visits completed to date\n",
|
143
182
|
"\n",
|
144
|
-
"df_tbl1 = df_visit[(df_visit.visit_code_sequence==0) & (df_visit.appt_timing==ONTIME_APPT) & ~(
|
183
|
+
"df_tbl1 = df_visit[(df_visit.visit_code_sequence == 0) & (df_visit.appt_timing == ONTIME_APPT) & ~(\n",
|
184
|
+
" df_visit.appt_status.isin([NEW_APPT, CANCELLED_APPT]))].groupby(\n",
|
185
|
+
" by=[\"visit_code\", \"site_id\"]).size().to_frame().reset_index()\n",
|
145
186
|
"\n",
|
146
187
|
"df_tbl1.columns = [\"visit_code\", \"site_id\", \"visits\"]\n",
|
147
188
|
"df1 = df_tbl1.pivot(index=\"visit_code\", columns=\"site_id\", values=\"visits\").reset_index()\n",
|
@@ -151,14 +192,14 @@
|
|
151
192
|
"df1.fillna(0, inplace=True)\n",
|
152
193
|
"df_attended = df1.copy().reset_index(drop=True)\n",
|
153
194
|
"df_attended = df_attended.fillna(0.0)"
|
154
|
-
]
|
155
|
-
"id": "9e3d608809eea5",
|
156
|
-
"outputs": [],
|
157
|
-
"execution_count": null
|
195
|
+
]
|
158
196
|
},
|
159
197
|
{
|
160
|
-
"metadata": {},
|
161
198
|
"cell_type": "code",
|
199
|
+
"execution_count": null,
|
200
|
+
"id": "7",
|
201
|
+
"metadata": {},
|
202
|
+
"outputs": [],
|
162
203
|
"source": [
|
163
204
|
"gt = df_as_great_table(\n",
|
164
205
|
" df_attended[[\"visit_code\", \"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]],\n",
|
@@ -166,7 +207,7 @@
|
|
166
207
|
")\n",
|
167
208
|
"gt = (\n",
|
168
209
|
" gt\n",
|
169
|
-
" .cols_label({k:v for k, v in column_headers.items() if k
|
210
|
+
" .cols_label({k: v for k, v in column_headers.items() if k != \"label\"})\n",
|
170
211
|
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
171
212
|
" .cols_align(align=\"left\", columns=[\"visit_code\"])\n",
|
172
213
|
" .data_color(\n",
|
@@ -175,18 +216,19 @@
|
|
175
216
|
" domain=[2000, 5000],\n",
|
176
217
|
" na_color=\"white\"\n",
|
177
218
|
" )\n",
|
178
|
-
" .tab_source_note(
|
219
|
+
" .tab_source_note(\n",
|
220
|
+
" source_note=f\"Excludes visit reports submitted for participants eventually withdrawn on late exclusion criteria.\")\n",
|
179
221
|
")\n",
|
180
222
|
"html_data.append(gt.as_raw_html())\n",
|
181
223
|
"gt.show()"
|
182
|
-
]
|
183
|
-
"id": "a43c2fbd8a7a692c",
|
184
|
-
"outputs": [],
|
185
|
-
"execution_count": null
|
224
|
+
]
|
186
225
|
},
|
187
226
|
{
|
188
|
-
"metadata": {},
|
189
227
|
"cell_type": "code",
|
228
|
+
"execution_count": null,
|
229
|
+
"id": "8",
|
230
|
+
"metadata": {},
|
231
|
+
"outputs": [],
|
190
232
|
"source": [
|
191
233
|
"# Table 1b Total scheduled appointments\n",
|
192
234
|
"df_appt_pivot = (\n",
|
@@ -204,7 +246,7 @@
|
|
204
246
|
" .fillna(0)\n",
|
205
247
|
")\n",
|
206
248
|
"\n",
|
207
|
-
"df_appt_pivot[\"total\"] = df_appt_pivot.iloc[:,1:].sum(axis=1)\n",
|
249
|
+
"df_appt_pivot[\"total\"] = df_appt_pivot.iloc[:, 1:].sum(axis=1)\n",
|
208
250
|
"df_appt_pivot.columns.name = None\n",
|
209
251
|
"gt = df_as_great_table(\n",
|
210
252
|
" df_appt_pivot,\n",
|
@@ -214,7 +256,7 @@
|
|
214
256
|
")\n",
|
215
257
|
"gt = (\n",
|
216
258
|
" gt\n",
|
217
|
-
" .cols_label({k:v for k, v in column_headers.items() if k
|
259
|
+
" .cols_label({k: v for k, v in column_headers.items() if k != \"label\"})\n",
|
218
260
|
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
219
261
|
" .cols_align(align=\"left\", columns=[\"visit_code\"])\n",
|
220
262
|
" .data_color(\n",
|
@@ -226,18 +268,19 @@
|
|
226
268
|
")\n",
|
227
269
|
"html_data.append(gt.as_raw_html())\n",
|
228
270
|
"gt.show()"
|
229
|
-
]
|
230
|
-
"id": "70eb34a139ff7095",
|
231
|
-
"outputs": [],
|
232
|
-
"execution_count": null
|
271
|
+
]
|
233
272
|
},
|
234
273
|
{
|
235
|
-
"metadata": {},
|
236
274
|
"cell_type": "code",
|
275
|
+
"execution_count": null,
|
276
|
+
"id": "9",
|
277
|
+
"metadata": {},
|
278
|
+
"outputs": [],
|
237
279
|
"source": [
|
238
280
|
"# Table 1c Past scheduled appointments -- no information provided\n",
|
239
281
|
"df_appt_pivot = (\n",
|
240
|
-
" df_appointments.query(\
|
282
|
+
" df_appointments.query(\n",
|
283
|
+
" \"appt_datetime<@cutoff_date and appt_reason==@SCHEDULED_APPT and appt_timing==@ONTIME_APPT and appt_status.isin([@NEW_APPT])\")\n",
|
241
284
|
" .merge(df_eos_excluded[[\"subject_identifier\"]], on=\"subject_identifier\", how=\"left\", indicator=True)\n",
|
242
285
|
" .query(\"_merge=='left_only'\")\n",
|
243
286
|
" .drop(columns=[\"_merge\"])\n",
|
@@ -250,7 +293,7 @@
|
|
250
293
|
" .reset_index()\n",
|
251
294
|
" .fillna(0)\n",
|
252
295
|
")\n",
|
253
|
-
"df_appt_pivot[\"total\"] = df_appt_pivot.iloc[:,1:].sum(axis=1)\n",
|
296
|
+
"df_appt_pivot[\"total\"] = df_appt_pivot.iloc[:, 1:].sum(axis=1)\n",
|
254
297
|
"df_appt_pivot.columns.name = None\n",
|
255
298
|
"gt = df_as_great_table(\n",
|
256
299
|
" df_appt_pivot,\n",
|
@@ -259,7 +302,7 @@
|
|
259
302
|
")\n",
|
260
303
|
"gt = (\n",
|
261
304
|
" gt\n",
|
262
|
-
" .cols_label({k:v for k, v in column_headers.items() if k
|
305
|
+
" .cols_label({k: v for k, v in column_headers.items() if k != \"label\"})\n",
|
263
306
|
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
264
307
|
" .cols_align(align=\"left\", columns=[\"visit_code\"])\n",
|
265
308
|
" .data_color(\n",
|
@@ -272,23 +315,23 @@
|
|
272
315
|
")\n",
|
273
316
|
"html_data.append(gt.as_raw_html())\n",
|
274
317
|
"gt.show()"
|
275
|
-
]
|
276
|
-
"id": "f243552177b216d7",
|
277
|
-
"outputs": [],
|
278
|
-
"execution_count": null
|
318
|
+
]
|
279
319
|
},
|
280
320
|
{
|
281
|
-
"metadata": {},
|
282
321
|
"cell_type": "code",
|
322
|
+
"execution_count": null,
|
323
|
+
"id": "10",
|
324
|
+
"metadata": {},
|
325
|
+
"outputs": [],
|
283
326
|
"source": [
|
284
327
|
"# Table 1d Unscheduled appointments\n",
|
285
328
|
"df_appt = (\n",
|
286
329
|
" df_appointments.query(\"appt_reason==@UNSCHEDULED_APPT and appt_timing==@ONTIME_APPT and appt_status!=@NEW_APPT\")\n",
|
287
|
-
"
|
330
|
+
" .merge(df_eos_excluded[[\"subject_identifier\"]], on=\"subject_identifier\", how=\"left\", indicator=True)\n",
|
288
331
|
" .query(\"_merge=='left_only'\")\n",
|
289
332
|
" .drop(columns=[\"_merge\"])\n",
|
290
333
|
" .reset_index(drop=True)\n",
|
291
|
-
"
|
334
|
+
" .copy()\n",
|
292
335
|
" .reset_index(drop=True)\n",
|
293
336
|
")\n",
|
294
337
|
"df_appt['visit_code'] = df_appt['visit_code'].astype(int)\n",
|
@@ -306,10 +349,10 @@
|
|
306
349
|
" .reset_index()\n",
|
307
350
|
" .fillna(0)\n",
|
308
351
|
")\n",
|
309
|
-
"df_appt_pivot[\"total\"] = df_appt_pivot.iloc[:,1:].sum(axis=1)\n",
|
352
|
+
"df_appt_pivot[\"total\"] = df_appt_pivot.iloc[:, 1:].sum(axis=1)\n",
|
310
353
|
"df_appt_pivot.columns.name = None\n",
|
311
|
-
"df_appt_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]] = df_appt_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]].astype(
|
312
|
-
"\n",
|
354
|
+
"df_appt_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]] = df_appt_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]].astype(\n",
|
355
|
+
" 'float64')\n",
|
313
356
|
"\n",
|
314
357
|
"# add totals row\n",
|
315
358
|
"sum_row = df_appt_pivot.select_dtypes(include='float64').sum()\n",
|
@@ -324,7 +367,7 @@
|
|
324
367
|
")\n",
|
325
368
|
"gt = (\n",
|
326
369
|
" gt\n",
|
327
|
-
" .cols_label({k:v for k, v in column_headers.items() if k
|
370
|
+
" .cols_label({k: v for k, v in column_headers.items() if k != \"label\"})\n",
|
328
371
|
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
329
372
|
" .cols_align(align=\"left\", columns=[\"visit_code\"])\n",
|
330
373
|
" .data_color(\n",
|
@@ -338,18 +381,19 @@
|
|
338
381
|
")\n",
|
339
382
|
"html_data.append(gt.as_raw_html())\n",
|
340
383
|
"gt.show()"
|
341
|
-
]
|
342
|
-
"id": "6e55569e322370a",
|
343
|
-
"outputs": [],
|
344
|
-
"execution_count": null
|
384
|
+
]
|
345
385
|
},
|
346
386
|
{
|
347
|
-
"metadata": {},
|
348
387
|
"cell_type": "code",
|
388
|
+
"execution_count": null,
|
389
|
+
"id": "11",
|
390
|
+
"metadata": {},
|
391
|
+
"outputs": [],
|
349
392
|
"source": [
|
350
393
|
"# Table 1e Future scheduled appointments\n",
|
351
394
|
"df_appt_pivot = (\n",
|
352
|
-
" df_appointments.query(\
|
395
|
+
" df_appointments.query(\n",
|
396
|
+
" \"@cutoff_date<=appt_datetime<@end_of_trial_date and appt_reason==@SCHEDULED_APPT and appt_timing==@ONTIME_APPT and appt_status.isin([@NEW_APPT])\")\n",
|
353
397
|
" .merge(df_eos_excluded[[\"subject_identifier\"]], on=\"subject_identifier\", how=\"left\", indicator=True)\n",
|
354
398
|
" .query(\"_merge=='left_only'\")\n",
|
355
399
|
" .drop(columns=[\"_merge\"])\n",
|
@@ -362,7 +406,7 @@
|
|
362
406
|
" .reset_index()\n",
|
363
407
|
" .fillna(0)\n",
|
364
408
|
")\n",
|
365
|
-
"df_appt_pivot[\"total\"] = df_appt_pivot.iloc[:,1:].sum(axis=1)\n",
|
409
|
+
"df_appt_pivot[\"total\"] = df_appt_pivot.iloc[:, 1:].sum(axis=1)\n",
|
366
410
|
"df_appt_pivot.columns.name = None\n",
|
367
411
|
"gt = df_as_great_table(\n",
|
368
412
|
" df_appt_pivot,\n",
|
@@ -370,7 +414,7 @@
|
|
370
414
|
")\n",
|
371
415
|
"gt = (\n",
|
372
416
|
" gt\n",
|
373
|
-
" .cols_label({k:v for k, v in column_headers.items() if k
|
417
|
+
" .cols_label({k: v for k, v in column_headers.items() if k != \"label\"})\n",
|
374
418
|
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
375
419
|
" .cols_align(align=\"left\", columns=[\"visit_code\"])\n",
|
376
420
|
" .data_color(\n",
|
@@ -380,18 +424,19 @@
|
|
380
424
|
" na_color=\"white\"\n",
|
381
425
|
" )\n",
|
382
426
|
" .fmt_number(columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"], decimals=0)\n",
|
383
|
-
" .tab_source_note(
|
427
|
+
" .tab_source_note(\n",
|
428
|
+
" source_note=f\"Scheduled appointment date is on or after {cutoff_date.strftime('%d %B %Y')} and before {end_of_trial_date.strftime('%d %B %Y')}.\")\n",
|
384
429
|
")\n",
|
385
430
|
"html_data.append(gt.as_raw_html())\n",
|
386
431
|
"gt.show()"
|
387
|
-
]
|
388
|
-
"id": "8193005de33cae6f",
|
389
|
-
"outputs": [],
|
390
|
-
"execution_count": null
|
432
|
+
]
|
391
433
|
},
|
392
434
|
{
|
393
|
-
"metadata": {},
|
394
435
|
"cell_type": "code",
|
436
|
+
"execution_count": null,
|
437
|
+
"id": "12",
|
438
|
+
"metadata": {},
|
439
|
+
"outputs": [],
|
395
440
|
"source": [
|
396
441
|
"# Table 2 Visits Missed to Date as % of Visits Attended + Visits Missed\n",
|
397
442
|
"subject_count = (\n",
|
@@ -403,7 +448,8 @@
|
|
403
448
|
" .query(\"visit_code_sequence==0 and appt_timing==@MISSED_APPT and ~appt_status.isin([@NEW_APPT, @CANCELLED_APPT])\")\n",
|
404
449
|
").subject_identifier.nunique()\n",
|
405
450
|
"df_tbl = (\n",
|
406
|
-
" df_visit[(df_visit.visit_code_sequence==0) & (df_visit.appt_timing==MISSED_APPT) & ~(
|
451
|
+
" df_visit[(df_visit.visit_code_sequence == 0) & (df_visit.appt_timing == MISSED_APPT) & ~(\n",
|
452
|
+
" df_visit.appt_status.isin([NEW_APPT, CANCELLED_APPT]))]\n",
|
407
453
|
" .merge(df_eos_excluded[[\"subject_identifier\"]], on=\"subject_identifier\", how=\"left\", indicator=True)\n",
|
408
454
|
" .query(\"_merge=='left_only'\")\n",
|
409
455
|
" .drop(columns=[\"_merge\"])\n",
|
@@ -439,7 +485,7 @@
|
|
439
485
|
" .set_index([\"visit_code\"])\n",
|
440
486
|
")\n",
|
441
487
|
"\n",
|
442
|
-
"attended_and_missed_perc = df_missed/attended_and_missed\n",
|
488
|
+
"attended_and_missed_perc = df_missed / attended_and_missed\n",
|
443
489
|
"attended_and_missed_perc = (\n",
|
444
490
|
" attended_and_missed_perc\n",
|
445
491
|
" .fillna(0)\n",
|
@@ -448,19 +494,19 @@
|
|
448
494
|
")\n",
|
449
495
|
"\n",
|
450
496
|
"df_result = df_missed.merge(attended_and_missed_perc, on=[\"visit_code\"], suffixes=(\"\", \"_perc\"))\n",
|
451
|
-
"for col in
|
497
|
+
"for col in [\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]:\n",
|
452
498
|
" col_perc = f\"{col}_perc\"\n",
|
453
|
-
" df_result[col] = df_result.apply(lambda x: f\"{x[col]} ({x[col_perc]*100:.2f})\", axis=1)\n",
|
499
|
+
" df_result[col] = df_result.apply(lambda x: f\"{x[col]} ({x[col_perc] * 100:.2f})\", axis=1)\n",
|
454
500
|
"df_result = df_result.reset_index().sort_values(by=[\"visit_code\"], ascending=True)\n",
|
455
501
|
"df_result = df_result.fillna(0.0)"
|
456
|
-
]
|
457
|
-
"id": "c86c5f0ffe59e951",
|
458
|
-
"outputs": [],
|
459
|
-
"execution_count": null
|
502
|
+
]
|
460
503
|
},
|
461
504
|
{
|
462
|
-
"metadata": {},
|
463
505
|
"cell_type": "code",
|
506
|
+
"execution_count": null,
|
507
|
+
"id": "13",
|
508
|
+
"metadata": {},
|
509
|
+
"outputs": [],
|
464
510
|
"source": [
|
465
511
|
"df_table = df_result[[\"visit_code\", \"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]].copy()\n",
|
466
512
|
"gt = df_as_great_table(\n",
|
@@ -470,7 +516,7 @@
|
|
470
516
|
")\n",
|
471
517
|
"gt = (\n",
|
472
518
|
" gt\n",
|
473
|
-
" .cols_label({k:v for k, v in column_headers.items() if k
|
519
|
+
" .cols_label({k: v for k, v in column_headers.items() if k != \"label\"})\n",
|
474
520
|
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
475
521
|
" .cols_align(align=\"left\", columns=[\"visit_code\", \"label\"])\n",
|
476
522
|
" .tab_style(\n",
|
@@ -485,14 +531,14 @@
|
|
485
531
|
")\n",
|
486
532
|
"html_data.append(gt.as_raw_html())\n",
|
487
533
|
"gt.show()\n"
|
488
|
-
]
|
489
|
-
"id": "3cd8b1290091660c",
|
490
|
-
"outputs": [],
|
491
|
-
"execution_count": null
|
534
|
+
]
|
492
535
|
},
|
493
536
|
{
|
494
|
-
"metadata": {},
|
495
537
|
"cell_type": "code",
|
538
|
+
"execution_count": null,
|
539
|
+
"id": "14",
|
540
|
+
"metadata": {},
|
541
|
+
"outputs": [],
|
496
542
|
"source": [
|
497
543
|
"# Table 2b: Number of missed visits by participant\n",
|
498
544
|
"subject_count = (\n",
|
@@ -504,7 +550,8 @@
|
|
504
550
|
" .query(\"visit_code_sequence==0 and appt_timing==@MISSED_APPT and ~appt_status.isin([@NEW_APPT, @CANCELLED_APPT])\")\n",
|
505
551
|
").subject_identifier.nunique()\n",
|
506
552
|
"df_tbl = (\n",
|
507
|
-
" df_visit[(df_visit.visit_code_sequence==0) & (df_visit.appt_timing==MISSED_APPT) & ~(
|
553
|
+
" df_visit[(df_visit.visit_code_sequence == 0) & (df_visit.appt_timing == MISSED_APPT) & ~(\n",
|
554
|
+
" df_visit.appt_status.isin([NEW_APPT, CANCELLED_APPT]))]\n",
|
508
555
|
" .merge(df_eos_excluded[[\"subject_identifier\"]], on=\"subject_identifier\", how=\"left\", indicator=True)\n",
|
509
556
|
" .query(\"_merge=='left_only'\")\n",
|
510
557
|
" .drop(columns=[\"_merge\"])\n",
|
@@ -515,15 +562,16 @@
|
|
515
562
|
" .reset_index()\n",
|
516
563
|
")\n",
|
517
564
|
"df_tbl.columns = [\"subject_identifier\", \"site_id\", \"missed_count\"]\n",
|
518
|
-
"df_tbl[\"category\"] = pd.cut(df_tbl[\"missed_count\"], bins=[0, 1, 3, 5, 7, 100]
|
519
|
-
"
|
565
|
+
"df_tbl[\"category\"] = pd.cut(df_tbl[\"missed_count\"], bins=[0, 1, 3, 5, 7, 100],\n",
|
566
|
+
" labels=[\"Missed at least 1\", \"2 to 3\", \"4 to 5\", \"6 to 7\", \"missed more than 7\"])\n",
|
567
|
+
"df_tbl_pivot = df_tbl.pivot_table(index=\"category\", columns=\"site_id\", values=\"missed_count\", observed=False,\n",
|
568
|
+
" aggfunc=\"count\").reset_index()\n",
|
520
569
|
"\n",
|
521
570
|
"df_tbl_pivot['total'] = df_tbl_pivot.select_dtypes(include='int').sum(axis=1, skipna=True)\n",
|
522
571
|
"\n",
|
523
572
|
"sum_row = df_tbl_pivot.select_dtypes(include='int64').sum()\n",
|
524
573
|
"sum_row['category'] = 'Total'\n",
|
525
574
|
"\n",
|
526
|
-
"\n",
|
527
575
|
"df_tbl_pivot = (\n",
|
528
576
|
" pd.concat([df_tbl_pivot, sum_row.to_frame().T], axis=0)\n",
|
529
577
|
" .rename(columns={10: \"10\", 20: \"20\", 30: \"30\", 40: \"40\", 60: \"60\"})\n",
|
@@ -535,7 +583,8 @@
|
|
535
583
|
")\n",
|
536
584
|
"gt = (\n",
|
537
585
|
" gt\n",
|
538
|
-
" .cols_label(
|
586
|
+
" .cols_label(\n",
|
587
|
+
" {\"category\": \"Category\", **{k: v for k, v in column_headers.items() if k not in [\"visit_code\", \"label\"]}})\n",
|
539
588
|
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
540
589
|
" .cols_align(align=\"left\", columns=[\"category\"])\n",
|
541
590
|
" .tab_style(\n",
|
@@ -549,17 +598,17 @@
|
|
549
598
|
"html_data.append(gt.as_raw_html())\n",
|
550
599
|
"gt.show()\n",
|
551
600
|
"\n"
|
552
|
-
]
|
553
|
-
"id": "b18088e16e0bf7f7",
|
554
|
-
"outputs": [],
|
555
|
-
"execution_count": null
|
601
|
+
]
|
556
602
|
},
|
557
603
|
{
|
558
|
-
"metadata": {},
|
559
604
|
"cell_type": "code",
|
605
|
+
"execution_count": null,
|
606
|
+
"id": "15",
|
607
|
+
"metadata": {},
|
608
|
+
"outputs": [],
|
560
609
|
"source": [
|
561
610
|
"# func for tables 3,4,5\n",
|
562
|
-
"def get_row_df(row_df:pd.DataFrame, label:str)->pd.DataFrame:\n",
|
611
|
+
"def get_row_df(row_df: pd.DataFrame, label: str = None, **kwargs) -> pd.DataFrame:\n",
|
563
612
|
" row_df = row_df.groupby(by=[\"site_id\"]).site_id.count().to_frame(name=\"n\")\n",
|
564
613
|
" row_df[\"label\"] = label\n",
|
565
614
|
" row_df = row_df.reset_index()\n",
|
@@ -573,52 +622,68 @@
|
|
573
622
|
" return row_df\n",
|
574
623
|
"\n",
|
575
624
|
"\n",
|
576
|
-
"def get_table_df(
|
625
|
+
"def get_table_df(\n",
|
626
|
+
" df_source: pd.DataFrame,\n",
|
627
|
+
" visit_code: float | None = None,\n",
|
628
|
+
" month_label: str | None = None,\n",
|
629
|
+
" visit_codes: list[float] | None = None,\n",
|
630
|
+
" get_row_func: Callable | None = None,\n",
|
631
|
+
" category_labels: list[str] | None = None,\n",
|
632
|
+
") -> pd.DataFrame:\n",
|
633
|
+
" get_row_df_func = get_row_func or get_row_df\n",
|
577
634
|
" if visit_code:\n",
|
578
|
-
" df_month = df_source[df_source.visit_code==visit_code].copy()\n",
|
635
|
+
" df_month = df_source[df_source.visit_code == visit_code].copy()\n",
|
636
|
+
" elif visit_codes:\n",
|
637
|
+
" df_month = df_source[df_source.visit_code.isin(visit_codes)].copy()\n",
|
579
638
|
" elif month_label:\n",
|
580
639
|
" df_month = df_source.copy()\n",
|
581
640
|
"\n",
|
582
|
-
" \n",
|
583
641
|
" row_df = df_month.copy()\n",
|
584
|
-
" table_df =
|
585
|
-
"
|
642
|
+
" table_df = get_row_df_func(row_df, \"Total (n)\", category_labels=category_labels)\n",
|
643
|
+
"\n",
|
586
644
|
" row_df = df_month.query(\"ogtt_value<7.8 and fbg_value<6.1\").copy()\n",
|
587
|
-
" table_df = pd.concat([table_df,
|
588
|
-
"
|
589
|
-
" row_df = df_month[(df_month.ogtt_value<7.8) & (df_month.fbg_value>=6.1) & (df_month.fbg_value<7.0)].copy()\n",
|
590
|
-
" table_df = pd.concat(
|
591
|
-
"
|
592
|
-
"
|
593
|
-
"
|
594
|
-
" \n",
|
595
|
-
"
|
596
|
-
"
|
597
|
-
" \n",
|
598
|
-
"
|
599
|
-
"
|
600
|
-
" \n",
|
601
|
-
"
|
602
|
-
" table_df = pd.concat(
|
603
|
-
"
|
604
|
-
"
|
605
|
-
"
|
606
|
-
" \n",
|
607
|
-
"
|
608
|
-
"
|
609
|
-
" \n",
|
610
|
-
"
|
611
|
-
"
|
645
|
+
" table_df = pd.concat([table_df, get_row_df_func(row_df, \"OGTT <7.8; FBG <6.1\", category_labels=category_labels)])\n",
|
646
|
+
"\n",
|
647
|
+
" row_df = df_month[(df_month.ogtt_value < 7.8) & (df_month.fbg_value >= 6.1) & (df_month.fbg_value < 7.0)].copy()\n",
|
648
|
+
" table_df = pd.concat(\n",
|
649
|
+
" [table_df, get_row_df_func(row_df, \"OGTT <7.8; FBG >=6.1 <7.0\", category_labels=category_labels)])\n",
|
650
|
+
"\n",
|
651
|
+
" row_df = df_month[(df_month.ogtt_value < 7.8) & (df_month.fbg_value >= 7.0)].copy()\n",
|
652
|
+
" table_df = pd.concat([table_df, get_row_df_func(row_df, \"OGTT <7.8; FBG >=7.0\", category_labels=category_labels)])\n",
|
653
|
+
"\n",
|
654
|
+
" row_df = df_month[(df_month.ogtt_value >= 7.8) & (df_month.ogtt_value < 11.1) & (df_month.fbg_value < 6.1)].copy()\n",
|
655
|
+
" table_df = pd.concat(\n",
|
656
|
+
" [table_df, get_row_df_func(row_df, \"OGTT ≥7.8 to <11.1; FBG <6.1\", category_labels=category_labels)])\n",
|
657
|
+
"\n",
|
658
|
+
" row_df = df_month[(df_month.ogtt_value >= 7.8) & (df_month.ogtt_value < 11.1) & (df_month.fbg_value >= 6.1) & (\n",
|
659
|
+
" df_month.fbg_value < 7.0)].copy()\n",
|
660
|
+
" table_df = pd.concat(\n",
|
661
|
+
" [table_df, get_row_df_func(row_df, \"OGTT ≥7.8 to <11.1; FBG >=6.1 <7.0\", category_labels=category_labels)])\n",
|
662
|
+
"\n",
|
663
|
+
" row_df = df_month[(df_month.ogtt_value >= 7.8) & (df_month.ogtt_value < 11.1) & (df_month.fbg_value >= 7.0)].copy()\n",
|
664
|
+
" table_df = pd.concat(\n",
|
665
|
+
" [table_df, get_row_df_func(row_df, \"OGTT ≥7.8 to <11.1; FBG >=7.0\", category_labels=category_labels)])\n",
|
666
|
+
"\n",
|
667
|
+
" row_df = df_month[(df_month.ogtt_value >= 11.1) & (df_month.fbg_value < 6.1)].copy()\n",
|
668
|
+
" table_df = pd.concat([table_df, get_row_df_func(row_df, \"OGTT ≥11.1; FBG <6.1\", category_labels=category_labels)])\n",
|
669
|
+
"\n",
|
670
|
+
" row_df = df_month[(df_month.ogtt_value >= 11.1) & (df_month.fbg_value >= 6.1) & (df_month.fbg_value < 7.0)].copy()\n",
|
671
|
+
" table_df = pd.concat(\n",
|
672
|
+
" [table_df, get_row_df_func(row_df, \"OGTT ≥11.1; FBG >=6.1 <7.0\", category_labels=category_labels)])\n",
|
673
|
+
"\n",
|
674
|
+
" row_df = df_month[(df_month.ogtt_value >= 11.1) & (df_month.fbg_value >= 7.0)].copy()\n",
|
675
|
+
" table_df = pd.concat([table_df, get_row_df_func(row_df, \"OGTT ≥11.1; FBG >=7.0\", category_labels=category_labels)])\n",
|
612
676
|
"\n",
|
613
677
|
" row_df = df_month[(df_month.ogtt_value.isna())].copy()\n",
|
614
|
-
" table_df = pd.concat([table_df,
|
678
|
+
" table_df = pd.concat([table_df, get_row_df_func(row_df, \"Missing OGTT\", category_labels=category_labels)])\n",
|
615
679
|
" return table_df\n",
|
616
680
|
"\n",
|
617
681
|
"\n",
|
618
|
-
"def format_table_df(tbl_df, add_totals:bool|None=None):\n",
|
682
|
+
"def format_table_df(tbl_df, add_totals: bool | None = None):\n",
|
683
|
+
" \"\"\"Pivot on site\"\"\"\n",
|
619
684
|
" add_totals = True if add_totals is None else add_totals\n",
|
620
685
|
" tbl_df = tbl_df.fillna(0.0)\n",
|
621
|
-
" tbl_df[\"total\"] = tbl_df.iloc[:,1:].sum(axis=1)\n",
|
686
|
+
" tbl_df[\"total\"] = tbl_df.iloc[:, 1:].sum(axis=1)\n",
|
622
687
|
" tbl_df = tbl_df.reset_index(drop=True)\n",
|
623
688
|
"\n",
|
624
689
|
" if add_totals:\n",
|
@@ -626,7 +691,7 @@
|
|
626
691
|
" df_last.loc[\"label\"] = np.nan\n",
|
627
692
|
" df_last = df_last.reset_index()\n",
|
628
693
|
" df_last.columns = [\"label\", \"value\"]\n",
|
629
|
-
" df_last = df_last.pivot_table(columns=\"label\",
|
694
|
+
" df_last = df_last.pivot_table(columns=\"label\", values=\"value\").reset_index(drop=True)\n",
|
630
695
|
" df_last.columns.name = \"\"\n",
|
631
696
|
" df_last[\"label\"] = \"Totals\"\n",
|
632
697
|
"\n",
|
@@ -636,55 +701,216 @@
|
|
636
701
|
" tbl_df.columns = [\"label\", \"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]\n",
|
637
702
|
"\n",
|
638
703
|
" for site in [\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]:\n",
|
639
|
-
" tbl_df[f\"{site}_perc\"] = (tbl_df[site]/tbl_df.iloc[0][site]) * 100 if tbl_df.iloc[0][site]>0 else 0\n",
|
704
|
+
" tbl_df[f\"{site}_perc\"] = (tbl_df[site] / tbl_df.iloc[0][site]) * 100 if tbl_df.iloc[0][site] > 0 else 0\n",
|
640
705
|
" tbl_df[f\"{site}_perc_str\"] = tbl_df[f\"{site}_perc\"].map('{:.1f}'.format)\n",
|
641
706
|
"\n",
|
642
|
-
"\n",
|
643
707
|
" for site in [\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]:\n",
|
644
|
-
" tbl_df[f\"{site}_str\"] = tbl_df[[f\"{site}\", f\"{site}_perc_str\"]].apply(lambda x: ' ('.join(x.astype(str))
|
708
|
+
" tbl_df[f\"{site}_str\"] = tbl_df[[f\"{site}\", f\"{site}_perc_str\"]].apply(lambda x: ' ('.join(x.astype(str)),\n",
|
709
|
+
" axis=1)\n",
|
645
710
|
" tbl_df[f\"{site}_str\"] = tbl_df[f\"{site}_str\"] + \")\"\n",
|
646
711
|
"\n",
|
647
712
|
" cols = [\"label\", *[f\"{site}_str\" for site in [\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]]]\n",
|
648
713
|
" tbl_df1 = tbl_df[cols]\n",
|
649
|
-
" tbl_df1.loc[tbl_df.label
|
650
|
-
"
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
714
|
+
" tbl_df1.loc[tbl_df.label == \"Total (n)\"] = tbl_df.iloc[0][\n",
|
715
|
+
" [\"label\", \"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]].to_list()\n",
|
716
|
+
" return tbl_df1\n",
|
717
|
+
"\n",
|
718
|
+
"\n",
|
719
|
+
"def format_table_with_bmi_df(tbl_df, add_totals: bool | None = None, category_labels: list[str] = None):\n",
|
720
|
+
" \"\"\"Pivot on BMI categories\"\"\"\n",
|
721
|
+
"\n",
|
722
|
+
" add_totals = True if add_totals is None else add_totals\n",
|
723
|
+
" tbl_df = tbl_df.fillna(0.0)\n",
|
724
|
+
" tbl_df[\"total\"] = tbl_df.iloc[:, 1:].sum(axis=1)\n",
|
725
|
+
" tbl_df = tbl_df.reset_index(drop=True)\n",
|
726
|
+
"\n",
|
727
|
+
" if add_totals:\n",
|
728
|
+
" df_last = tbl_df[1:].sum().to_frame()\n",
|
729
|
+
" df_last.loc[\"label\"] = np.nan\n",
|
730
|
+
" df_last = df_last.reset_index()\n",
|
731
|
+
" df_last.columns = [\"label\", \"value\"]\n",
|
732
|
+
" df_last = df_last.pivot_table(columns=\"label\", values=\"value\").reset_index(drop=True)\n",
|
733
|
+
" df_last.columns.name = \"\"\n",
|
734
|
+
" df_last[\"label\"] = \"Totals\"\n",
|
735
|
+
"\n",
|
736
|
+
" tbl_df = pd.concat([tbl_df, df_last])\n",
|
737
|
+
" tbl_df = tbl_df.reset_index(drop=True)\n",
|
738
|
+
"\n",
|
739
|
+
" tbl_df.columns = [\"label\", *category_labels, \"total\"]\n",
|
740
|
+
"\n",
|
741
|
+
" for label in [*category_labels, \"total\"]:\n",
|
742
|
+
" tbl_df[f\"{label}_perc\"] = (tbl_df[label] / tbl_df.iloc[0][label]) * 100 if tbl_df.iloc[0][label] > 0 else 0\n",
|
743
|
+
" tbl_df[f\"{label}_perc_str\"] = tbl_df[f\"{label}_perc\"].map('{:.1f}'.format)\n",
|
744
|
+
"\n",
|
745
|
+
" for cat in [*category_labels, \"total\"]:\n",
|
746
|
+
" tbl_df[f\"{label}_str\"] = tbl_df[[f\"{label}\", f\"{label}_perc_str\"]].apply(lambda x: ' ('.join(x.astype(str)),\n",
|
747
|
+
" axis=1)\n",
|
748
|
+
" tbl_df[f\"{label}_str\"] = tbl_df[f\"{label}_str\"] + \")\"\n",
|
749
|
+
"\n",
|
750
|
+
" cols = [\"label\", *[f\"{label}_str\" for label in [*category_labels, \"total\"]]]\n",
|
751
|
+
" tbl_df1 = tbl_df[cols]\n",
|
752
|
+
" tbl_df1.loc[tbl_df.label == \"Total (n)\"] = tbl_df.iloc[0][[\"label\", *category_labels, \"total\"]].to_list()\n",
|
753
|
+
" return tbl_df1\n",
|
754
|
+
"\n",
|
755
|
+
"\n",
|
756
|
+
"def get_row_by_df(row_df: pd.DataFrame, label: str, category_labels: list[str]) -> pd.DataFrame:\n",
|
757
|
+
" # if label not in category_labels:\n",
|
758
|
+
" # raise ValueError(f\"Invalid label. Expected one of {category_labels}. Got {label}.\")\n",
|
759
|
+
" row_df = row_df.groupby(by=[\"site_id\"]).site_id.count().to_frame(name=\"n\")\n",
|
760
|
+
" row_df[\"label\"] = label\n",
|
761
|
+
" row_df = row_df.reset_index()\n",
|
762
|
+
" row_df = row_df.pivot(index=\"label\", values=\"n\", columns=\"site_id\").reset_index()\n",
|
763
|
+
" row_df.columns.name = \"\"\n",
|
764
|
+
"\n",
|
765
|
+
" for label in category_labels:\n",
|
766
|
+
" if label not in row_df.columns:\n",
|
767
|
+
" row_df[label] = None\n",
|
768
|
+
" row_df = row_df.reset_index(drop=True)\n",
|
769
|
+
" return row_df"
|
770
|
+
]
|
655
771
|
},
|
656
772
|
{
|
773
|
+
"cell_type": "code",
|
774
|
+
"execution_count": null,
|
775
|
+
"id": "16",
|
657
776
|
"metadata": {},
|
777
|
+
"outputs": [],
|
778
|
+
"source": [
|
779
|
+
"def get_fbg_value(r):\n",
|
780
|
+
" if not pd.isna(r[\"converted_fbg2_value\"]):\n",
|
781
|
+
" return r[\"converted_fbg2_value\"]\n",
|
782
|
+
" return r[\"converted_fbg_value\"]\n",
|
783
|
+
"\n",
|
784
|
+
"\n",
|
785
|
+
"def get_ogtt_value(r):\n",
|
786
|
+
" if not pd.isna(r[\"converted_ogtt2_value\"]):\n",
|
787
|
+
" return r[\"converted_ogtt2_value\"]\n",
|
788
|
+
" return r[\"converted_ogtt_value\"]\n"
|
789
|
+
]
|
790
|
+
},
|
791
|
+
{
|
658
792
|
"cell_type": "code",
|
793
|
+
"execution_count": null,
|
794
|
+
"id": "17",
|
795
|
+
"metadata": {},
|
796
|
+
"outputs": [],
|
659
797
|
"source": [
|
660
|
-
"# Table 3: OGTT and FBG at
|
661
|
-
"
|
798
|
+
"# Table 3: OGTT and FBG at Enrolment\n",
|
799
|
+
"\n",
|
800
|
+
"subjects = df_visit.subject_identifier.unique()\n",
|
801
|
+
"df_screening = get_screening_df().query(\"consented==True and subject_identifier.isin(@subjects)\")\n",
|
802
|
+
"df_screening[\"visit_code\"] = \"Enrol\"\n",
|
803
|
+
"df_screening[\"fbg_value\"] = df_screening.apply(get_fbg_value, axis=1)\n",
|
804
|
+
"df_screening[\"ogtt_value\"] = df_screening.apply(get_ogtt_value, axis=1)\n",
|
805
|
+
"df_screening[\"site_id\"] = df_screening.site.astype(int)\n",
|
806
|
+
"df_screening = df_screening.drop(columns=[\"site\"])\n",
|
807
|
+
"df_table3 = get_table_df(df_screening, month_label=\"enrol\")\n",
|
662
808
|
"df_table3 = format_table_df(df_table3)\n",
|
663
809
|
"df_table3 = df_table3.fillna(0.0)\n",
|
664
|
-
"gt = df_as_great_table(df_table3, title=\"Table
|
810
|
+
"gt = df_as_great_table(df_table3, title=\"Table 3a: OGTT and FBG at Screening / Enrolment\")\n",
|
811
|
+
"\n",
|
812
|
+
"column_headers_enrol = {k: v for k, v in column_headers_with_str.items() if k not in \"visit_code\"}\n",
|
665
813
|
"gt = (\n",
|
666
814
|
" gt\n",
|
667
|
-
" .cols_label(
|
815
|
+
" .cols_label(column_headers_enrol)\n",
|
668
816
|
" .cols_align(align=\"center\", columns=[\"10_str\", \"20_str\", \"30_str\", \"40_str\", \"60_str\", \"total_str\"])\n",
|
669
817
|
" .cols_align(align=\"left\", columns=[\"label\"])\n",
|
670
818
|
" .cols_width(cases={\"label\": \"35%\"})\n",
|
819
|
+
" .tab_source_note(source_note=\"Excluding patients eventually withdrawn for `late exclusion` criteria\")\n",
|
671
820
|
")\n",
|
672
821
|
"html_data.append(gt.as_raw_html())\n",
|
673
822
|
"gt.show()\n"
|
674
|
-
]
|
675
|
-
|
823
|
+
]
|
824
|
+
},
|
825
|
+
{
|
826
|
+
"cell_type": "code",
|
827
|
+
"execution_count": null,
|
828
|
+
"id": "18",
|
829
|
+
"metadata": {},
|
830
|
+
"outputs": [],
|
831
|
+
"source": [
|
832
|
+
"# bmi_categories:\n",
|
833
|
+
"# 1 calculated_bmi_value<25\n",
|
834
|
+
"# 2 calculated_bmi_value>=25 & calculated_bmi_value<30\n",
|
835
|
+
"# 3 calculated_bmi_value>=30\n",
|
836
|
+
"\n",
|
837
|
+
"\n",
|
838
|
+
"# subjects = df_visit.subject_identifier.unique()\n",
|
839
|
+
"# df_screening = get_screening_df().query(\"consented==True and subject_identifier.isin(@subjects)\")\n",
|
840
|
+
"# df_screening[\"visit_code\"] = \"Enrol\"\n",
|
841
|
+
"# df_screening[\"fbg_value\"] = df_screening.apply(get_fbg_value, axis=1)\n",
|
842
|
+
"# df_screening[\"ogtt_value\"] = df_screening.apply(get_ogtt_value, axis=1)\n",
|
843
|
+
"# df_screening[\"site_id\"] = df_screening.site.astype(int)\n",
|
844
|
+
"# df_screening = df_screening.drop(columns=[\"site\"])\n",
|
845
|
+
"# df_screening[\"bmi\"] = pd.NA\n",
|
846
|
+
"# df_screening.loc[df_screening[\"calculated_bmi_value\"] < 25.0, \"bmi\"] = \"bmi<25\"\n",
|
847
|
+
"# df_screening.loc[(df_screening[\"calculated_bmi_value\"]>=25.0) & (df_screening[\"calculated_bmi_value\"] < 30.0), \"bmi\"] = \"25<=bmi<30\"\n",
|
848
|
+
"# df_screening.loc[df_screening[\"calculated_bmi_value\"] > 30.0, \"bmi\"] = \"bmi>30\"\n",
|
849
|
+
"#\n",
|
850
|
+
"# category_labels = [ \"bmi<25\", \"25<=bmi<30\", \"bmi>=30\", \"Total (n)\"]\n",
|
851
|
+
"# df_table3 = get_table_df(df_screening, month_label=\"enrol\", get_row_func=get_row_by_df, category_labels=category_labels)\n",
|
852
|
+
"# df_table3 = format_table_with_bmi_df(df_table3, category_labels=category_labels)\n",
|
853
|
+
"# df_table3 = df_table3.fillna(0.0)\n",
|
854
|
+
"# gt = df_as_great_table(df_table3, title=\"Table 3b: OGTT/FBG by BMI at Screening / Enrolment\")\n",
|
855
|
+
"# column_headers_enrol = {\"bmi<25_str\":\"bmi<25\", \"25<=bmi<30_str\":\"25<=bmi<30\", \"bmi>30_str\":\"bmi>30\", \"total_str\": \"total\"}\n",
|
856
|
+
"# gt = (\n",
|
857
|
+
"# gt\n",
|
858
|
+
"# .cols_label(column_headers_enrol)\n",
|
859
|
+
"# .cols_align(align=\"center\", columns=[\"bmi<25_str\", \"25<=bmi<30_str\", \"bmi>30_str\", \"total_str\"])\n",
|
860
|
+
"# .cols_align(align=\"left\", columns=[\"label\"])\n",
|
861
|
+
"# .cols_width(cases={\"label\": \"35%\"})\n",
|
862
|
+
"# .tab_source_note(source_note=\"Excluding patients eventually withdrawn for `late exclusion` criteria\")\n",
|
863
|
+
"# )\n",
|
864
|
+
"# html_data.append(gt.as_raw_html())\n",
|
865
|
+
"# gt.show()\n",
|
866
|
+
"\n"
|
867
|
+
]
|
868
|
+
},
|
869
|
+
{
|
870
|
+
"cell_type": "code",
|
871
|
+
"execution_count": null,
|
872
|
+
"id": "19",
|
873
|
+
"metadata": {},
|
676
874
|
"outputs": [],
|
677
|
-
"
|
875
|
+
"source": [
|
876
|
+
"[col for col in df_screening.columns if \"bmi\" in col]"
|
877
|
+
]
|
678
878
|
},
|
679
879
|
{
|
880
|
+
"cell_type": "code",
|
881
|
+
"execution_count": null,
|
882
|
+
"id": "20",
|
680
883
|
"metadata": {},
|
884
|
+
"outputs": [],
|
885
|
+
"source": [
|
886
|
+
"# Table 4: OGTT and FBG at 12-month visit\n",
|
887
|
+
"df_table3 = get_table_df(df_glucose, visit_codes=[1120.0])\n",
|
888
|
+
"df_table3 = format_table_df(df_table3)\n",
|
889
|
+
"df_table3 = df_table3.fillna(0.0)\n",
|
890
|
+
"gt = df_as_great_table(df_table3, title=\"Table 4: OGTT and FBG at 12-month visit\")\n",
|
891
|
+
"gt = (\n",
|
892
|
+
" gt\n",
|
893
|
+
" .cols_label(column_headers_with_str)\n",
|
894
|
+
" .cols_align(align=\"center\", columns=[\"10_str\", \"20_str\", \"30_str\", \"40_str\", \"60_str\", \"total_str\"])\n",
|
895
|
+
" .cols_align(align=\"left\", columns=[\"label\"])\n",
|
896
|
+
" .cols_width(cases={\"label\": \"35%\"})\n",
|
897
|
+
")\n",
|
898
|
+
"html_data.append(gt.as_raw_html())\n",
|
899
|
+
"gt.show()\n"
|
900
|
+
]
|
901
|
+
},
|
902
|
+
{
|
681
903
|
"cell_type": "code",
|
904
|
+
"execution_count": null,
|
905
|
+
"id": "21",
|
906
|
+
"metadata": {},
|
907
|
+
"outputs": [],
|
682
908
|
"source": [
|
683
|
-
"# Table
|
909
|
+
"# Table 5: OGTT and FBG at 24-month visit\n",
|
684
910
|
"df_table4 = get_table_df(df_glucose, 1240.0)\n",
|
685
911
|
"df_table4 = format_table_df(df_table4)\n",
|
686
912
|
"df_table4 = df_table4.fillna(0.0)\n",
|
687
|
-
"gt = df_as_great_table(df_table4, title=\"Table
|
913
|
+
"gt = df_as_great_table(df_table4, title=\"Table 5: OGTT and FBG at 24-month visit\")\n",
|
688
914
|
"gt = (\n",
|
689
915
|
" gt\n",
|
690
916
|
" .cols_label(column_headers_with_str)\n",
|
@@ -694,20 +920,20 @@
|
|
694
920
|
")\n",
|
695
921
|
"html_data.append(gt.as_raw_html())\n",
|
696
922
|
"gt.show()"
|
697
|
-
]
|
698
|
-
"id": "ec0988364166e130",
|
699
|
-
"outputs": [],
|
700
|
-
"execution_count": null
|
923
|
+
]
|
701
924
|
},
|
702
925
|
{
|
703
|
-
"metadata": {},
|
704
926
|
"cell_type": "code",
|
927
|
+
"execution_count": null,
|
928
|
+
"id": "22",
|
929
|
+
"metadata": {},
|
930
|
+
"outputs": [],
|
705
931
|
"source": [
|
706
|
-
"# Table
|
932
|
+
"# Table 6: OGTT and FBG at 36-month visit\n",
|
707
933
|
"df_table5 = get_table_df(df_glucose, 1360.0)\n",
|
708
934
|
"df_table5 = format_table_df(df_table5)\n",
|
709
935
|
"df_table5 = df_table5.fillna(0.0)\n",
|
710
|
-
"gt = df_as_great_table(df_table5, title=\"Table
|
936
|
+
"gt = df_as_great_table(df_table5, title=\"Table 6: OGTT and FBG at 36-month visit\")\n",
|
711
937
|
"gt = (\n",
|
712
938
|
" gt\n",
|
713
939
|
" .cols_label(column_headers_with_str)\n",
|
@@ -717,21 +943,21 @@
|
|
717
943
|
")\n",
|
718
944
|
"html_data.append(gt.as_raw_html())\n",
|
719
945
|
"gt.show()"
|
720
|
-
]
|
721
|
-
"id": "59be72121202df15",
|
722
|
-
"outputs": [],
|
723
|
-
"execution_count": null
|
946
|
+
]
|
724
947
|
},
|
725
948
|
{
|
726
|
-
"metadata": {},
|
727
949
|
"cell_type": "code",
|
950
|
+
"execution_count": null,
|
951
|
+
"id": "23",
|
952
|
+
"metadata": {},
|
953
|
+
"outputs": [],
|
728
954
|
"source": [
|
729
|
-
"# Table
|
730
|
-
"row_df = df_glucose[df_glucose.ogtt_value>=11.1].copy()\n",
|
955
|
+
"# Table 7: Any OGTT>11.1 ever\n",
|
956
|
+
"row_df = df_glucose[df_glucose.ogtt_value >= 11.1].copy()\n",
|
731
957
|
"table_df = get_row_df(row_df, \"Total (n)\")\n",
|
732
958
|
"df_table6 = format_table_df(table_df)\n",
|
733
959
|
"df_table = df_table6[:1].fillna(0.0).copy().reset_index(drop=True)\n",
|
734
|
-
"gt = df_as_great_table(df_table, title=\"Table
|
960
|
+
"gt = df_as_great_table(df_table, title=\"Table 7: Any OGTT>11.1 ever\")\n",
|
735
961
|
"gt = (\n",
|
736
962
|
" gt\n",
|
737
963
|
" .cols_label(column_headers_with_str)\n",
|
@@ -741,41 +967,41 @@
|
|
741
967
|
")\n",
|
742
968
|
"html_data.append(gt.as_raw_html())\n",
|
743
969
|
"gt.show()"
|
744
|
-
]
|
745
|
-
"id": "f016ddbe736c2f93",
|
746
|
-
"outputs": [],
|
747
|
-
"execution_count": null
|
970
|
+
]
|
748
971
|
},
|
749
972
|
{
|
750
|
-
"metadata": {},
|
751
973
|
"cell_type": "code",
|
974
|
+
"execution_count": null,
|
975
|
+
"id": "24",
|
976
|
+
"metadata": {},
|
977
|
+
"outputs": [],
|
752
978
|
"source": [
|
753
979
|
"# func for table 7\n",
|
754
|
-
"def get_table7_df(df_source:pd.DataFrame, visit_code:float)->pd.DataFrame:\n",
|
755
|
-
" df_month = df_source[(df_source.visit_code>=visit_code) & (df_source.visit_code<=visit_code + 0.9)].copy()\n",
|
980
|
+
"def get_table7_df(df_source: pd.DataFrame, visit_code: float) -> pd.DataFrame:\n",
|
981
|
+
" df_month = df_source[(df_source.visit_code >= visit_code) & (df_source.visit_code <= visit_code + 0.9)].copy()\n",
|
756
982
|
"\n",
|
757
983
|
" row_df = df_month.copy()\n",
|
758
984
|
" table_df = get_row_df(row_df, \"Total (n)\")\n",
|
759
985
|
"\n",
|
760
|
-
" row_df = df_month[(df_month.fbg_value<6.1)].copy()\n",
|
986
|
+
" row_df = df_month[(df_month.fbg_value < 6.1)].copy()\n",
|
761
987
|
" table_df = pd.concat([table_df, get_row_df(row_df, \"FBG <6.1\")])\n",
|
762
988
|
"\n",
|
763
|
-
" row_df = df_month[(df_month.fbg_value>=6.1) & (df_month.fbg_value<7.0)].copy()\n",
|
989
|
+
" row_df = df_month[(df_month.fbg_value >= 6.1) & (df_month.fbg_value < 7.0)].copy()\n",
|
764
990
|
" table_df = pd.concat([table_df, get_row_df(row_df, \"FBG >=6.1 <7.0\")])\n",
|
765
991
|
"\n",
|
766
|
-
" row_df = df_month[(df_month.fbg_value>=7.0)].copy()\n",
|
992
|
+
" row_df = df_month[(df_month.fbg_value >= 7.0)].copy()\n",
|
767
993
|
" table_df = pd.concat([table_df, get_row_df(row_df, \"FBG >=7.0\")])\n",
|
768
994
|
" return table_df"
|
769
|
-
]
|
770
|
-
"id": "6193907cc12f5b5c",
|
771
|
-
"outputs": [],
|
772
|
-
"execution_count": null
|
995
|
+
]
|
773
996
|
},
|
774
997
|
{
|
775
|
-
"metadata": {},
|
776
998
|
"cell_type": "code",
|
999
|
+
"execution_count": null,
|
1000
|
+
"id": "25",
|
1001
|
+
"metadata": {},
|
1002
|
+
"outputs": [],
|
777
1003
|
"source": [
|
778
|
-
"# Table
|
1004
|
+
"# Table 8: Interim FBG results\n",
|
779
1005
|
"df_table7 = get_table7_df(df_glucose, 1150.0)\n",
|
780
1006
|
"df_table7 = format_table_df(df_table7, add_totals=False)\n",
|
781
1007
|
"df_table7[\"visit_code\"] = MONTH15\n",
|
@@ -807,17 +1033,17 @@
|
|
807
1033
|
"df_table = pd.concat([df_table7, df_table71, df_table72, df_table73, df_table74, df_table75, df_table76])\n",
|
808
1034
|
"df_table = df_table.reset_index(drop=True)\n",
|
809
1035
|
"df_table = df_table.fillna(0.0)"
|
810
|
-
]
|
811
|
-
"id": "16adb1f965081358",
|
812
|
-
"outputs": [],
|
813
|
-
"execution_count": null
|
1036
|
+
]
|
814
1037
|
},
|
815
1038
|
{
|
816
|
-
"metadata": {},
|
817
1039
|
"cell_type": "code",
|
1040
|
+
"execution_count": null,
|
1041
|
+
"id": "26",
|
1042
|
+
"metadata": {},
|
1043
|
+
"outputs": [],
|
818
1044
|
"source": [
|
819
1045
|
"column_headers_with_str = {\"visit_code\": \"Visit Code\", **column_headers_with_str}\n",
|
820
|
-
"gt = df_as_great_table2(df_table, title=\"Table
|
1046
|
+
"gt = df_as_great_table2(df_table, title=\"Table 8: Interim FBG results\")\n",
|
821
1047
|
"gt = (\n",
|
822
1048
|
" gt\n",
|
823
1049
|
" .cols_label(column_headers_with_str)\n",
|
@@ -835,63 +1061,69 @@
|
|
835
1061
|
")\n",
|
836
1062
|
"html_data.append(gt.as_raw_html())\n",
|
837
1063
|
"gt.show()"
|
838
|
-
]
|
839
|
-
"id": "81bcfe52d364b646",
|
840
|
-
"outputs": [],
|
841
|
-
"execution_count": null
|
1064
|
+
]
|
842
1065
|
},
|
843
1066
|
{
|
844
|
-
"metadata": {},
|
845
1067
|
"cell_type": "code",
|
1068
|
+
"execution_count": null,
|
1069
|
+
"id": "27",
|
1070
|
+
"metadata": {},
|
1071
|
+
"outputs": [],
|
846
1072
|
"source": [
|
847
|
-
"# Table
|
1073
|
+
"# Table 9: Primary Endpoint met\n",
|
848
1074
|
"df_endpoint_grp = df_endpoint.groupby(by=[\"site_id\", \"endpoint_label\"]).size().to_frame().reset_index()\n",
|
849
1075
|
"df_endpoint_grp.columns = [\"site_id\", \"label\", \"endpoints\"]\n",
|
850
1076
|
"df_endpoint_pivot = df_endpoint_grp.pivot_table(index=\"label\", columns=\"site_id\", values=\"endpoints\").reset_index()\n",
|
851
1077
|
"df_endpoint_pivot.columns.name = \"\"\n",
|
852
1078
|
"df_endpoint_pivot.columns = ['label', \"10\", \"20\", \"30\", \"40\", \"60\"]\n",
|
853
1079
|
"df_endpoint_pivot.loc[len(df_endpoint_pivot)] = df_endpoint_pivot[['10', '20', '30', '40', '60']].sum().to_dict()\n",
|
854
|
-
"df_endpoint_pivot.at[len(df_endpoint_pivot)-1, 'label'] = 'Total'\n",
|
1080
|
+
"df_endpoint_pivot.at[len(df_endpoint_pivot) - 1, 'label'] = 'Total'\n",
|
855
1081
|
"df_endpoint_pivot['total'] = df_endpoint_pivot[['10', '20', '30', '40', '60']].sum(axis=1)\n",
|
856
1082
|
"df_endpoint_pivot = df_endpoint_pivot.fillna(0.0)\n",
|
857
1083
|
"\n",
|
858
1084
|
"gt = df_as_great_table(\n",
|
859
1085
|
" df_endpoint_pivot,\n",
|
860
|
-
" title=\"Table
|
1086
|
+
" title=\"Table 9a: Primary Endpoint met\"\n",
|
861
1087
|
")\n",
|
862
1088
|
"gt = (\n",
|
863
1089
|
" gt\n",
|
864
|
-
" .cols_label({k:v for k, v in column_headers.items() if k not in [\"visit_code\"]})\n",
|
1090
|
+
" .cols_label({k: v for k, v in column_headers.items() if k not in [\"visit_code\"]})\n",
|
865
1091
|
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
866
1092
|
" .cols_align(align=\"left\", columns=[\"label\"])\n",
|
867
1093
|
" .cols_width(cases={\"label\": \"25%\"})\n",
|
868
1094
|
")\n",
|
869
1095
|
"html_data.append(gt.as_raw_html())\n",
|
870
1096
|
"gt.show()"
|
871
|
-
]
|
872
|
-
"id": "37904c7ce49724e6",
|
873
|
-
"outputs": [],
|
874
|
-
"execution_count": null
|
1097
|
+
]
|
875
1098
|
},
|
876
1099
|
{
|
877
|
-
"metadata": {},
|
878
1100
|
"cell_type": "code",
|
879
|
-
"
|
880
|
-
"id": "
|
1101
|
+
"execution_count": null,
|
1102
|
+
"id": "28",
|
1103
|
+
"metadata": {},
|
881
1104
|
"outputs": [],
|
882
|
-
"
|
1105
|
+
"source": [
|
1106
|
+
"#read_frame(SubjectScheduleHistory.objects.filter(offschedule_model=\"meta_prn.offschedule\"), verbose=False).rename(columns={\"site\": \"site_id\"})"
|
1107
|
+
]
|
883
1108
|
},
|
884
1109
|
{
|
885
|
-
"metadata": {},
|
886
1110
|
"cell_type": "code",
|
1111
|
+
"execution_count": null,
|
1112
|
+
"id": "29",
|
1113
|
+
"metadata": {},
|
1114
|
+
"outputs": [],
|
887
1115
|
"source": [
|
888
1116
|
"from great_tables import html\n",
|
889
1117
|
"\n",
|
890
|
-
"# Table
|
891
|
-
"df_subjecthistory = read_frame(
|
1118
|
+
"# Table 9b: Primary Endpoint no EOS or DM Referral\n",
|
1119
|
+
"df_subjecthistory = read_frame(\n",
|
1120
|
+
" SubjectScheduleHistory.objects.filter(offschedule_model=\"meta_prn.offschedule\", offschedule_datetime__isnull=False),\n",
|
1121
|
+
" verbose=False).rename(columns={\"site\": \"site_id\"})\n",
|
892
1122
|
"df_subjecthistory[\"site_id\"] = df_subjecthistory[\"site_id\"].astype(str)\n",
|
893
|
-
"df_endpoint_no_off = df_endpoint.merge(df_subjecthistory[[\"subject_identifier\", \"offschedule_datetime\"]]
|
894
|
-
"
|
1123
|
+
"df_endpoint_no_off = df_endpoint.merge(df_subjecthistory[[\"subject_identifier\", \"offschedule_datetime\"]],\n",
|
1124
|
+
" on=[\"subject_identifier\"], how=\"left\")\n",
|
1125
|
+
"df_endpoint_grp = df_endpoint_no_off.query(\"offschedule_datetime.isna()\").groupby(\n",
|
1126
|
+
" by=[\"site_id\", \"endpoint_label\"]).size().to_frame().reset_index()\n",
|
895
1127
|
"df_endpoint_grp.columns = [\"site_id\", \"label\", \"endpoints\"]\n",
|
896
1128
|
"df_endpoint_pivot = df_endpoint_grp.pivot_table(index=\"label\", columns=\"site_id\", values=\"endpoints\").reset_index()\n",
|
897
1129
|
"df_endpoint_pivot.columns.name = \"\"\n",
|
@@ -900,18 +1132,18 @@
|
|
900
1132
|
" df_endpoint_pivot[str(col)] = np.nan\n",
|
901
1133
|
"df_endpoint_pivot.columns = ['label', \"10\", \"20\", \"30\", \"40\", \"60\"]\n",
|
902
1134
|
"df_endpoint_pivot.loc[len(df_endpoint_pivot)] = df_endpoint_pivot[['10', '20', '30', '40', '60']].sum().to_dict()\n",
|
903
|
-
"df_endpoint_pivot.at[len(df_endpoint_pivot)-1, 'label'] = 'Total'\n",
|
1135
|
+
"df_endpoint_pivot.at[len(df_endpoint_pivot) - 1, 'label'] = 'Total'\n",
|
904
1136
|
"df_endpoint_pivot['total'] = df_endpoint_pivot[['10', '20', '30', '40', '60']].sum(axis=1)\n",
|
905
1137
|
"df_endpoint_pivot = df_endpoint_pivot.fillna(0.0)\n",
|
906
1138
|
"subjects = df_endpoint_no_off.query(\"offschedule_datetime.isna()\").subject_identifier.to_list()\n",
|
907
1139
|
"\n",
|
908
1140
|
"gt = df_as_great_table(\n",
|
909
1141
|
" df_endpoint_pivot,\n",
|
910
|
-
" title=\"Table
|
1142
|
+
" title=\"Table 9b: Primary Endpoint met -- participant not referred\"\n",
|
911
1143
|
")\n",
|
912
1144
|
"gt = (\n",
|
913
1145
|
" gt\n",
|
914
|
-
" .cols_label({k:v for k, v in column_headers.items() if k not in [\"visit_code\"]})\n",
|
1146
|
+
" .cols_label({k: v for k, v in column_headers.items() if k not in [\"visit_code\"]})\n",
|
915
1147
|
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
916
1148
|
" .cols_align(align=\"left\", columns=[\"label\"])\n",
|
917
1149
|
" .cols_width(cases={\"label\": \"25%\"})\n",
|
@@ -919,31 +1151,32 @@
|
|
919
1151
|
")\n",
|
920
1152
|
"html_data.append(gt.as_raw_html())\n",
|
921
1153
|
"gt.show()"
|
922
|
-
]
|
923
|
-
"id": "a74cd253ec5827f9",
|
924
|
-
"outputs": [],
|
925
|
-
"execution_count": null
|
1154
|
+
]
|
926
1155
|
},
|
927
1156
|
{
|
928
|
-
"metadata": {},
|
929
1157
|
"cell_type": "code",
|
930
|
-
"
|
931
|
-
"id": "
|
1158
|
+
"execution_count": null,
|
1159
|
+
"id": "30",
|
1160
|
+
"metadata": {},
|
932
1161
|
"outputs": [],
|
933
|
-
"
|
1162
|
+
"source": []
|
934
1163
|
},
|
935
1164
|
{
|
936
|
-
"metadata": {},
|
937
1165
|
"cell_type": "code",
|
1166
|
+
"execution_count": null,
|
1167
|
+
"id": "31",
|
1168
|
+
"metadata": {},
|
1169
|
+
"outputs": [],
|
938
1170
|
"source": [
|
939
|
-
"# Table
|
1171
|
+
"# Table 10: Incident Rate per 1000 person years\n",
|
940
1172
|
"\n",
|
941
|
-
"def get_df_main(df_visit:pd.DataFrame, lower_days:float|None=None, upper_days:float|None=None):\n",
|
1173
|
+
"def get_df_main(df_visit: pd.DataFrame, lower_days: float | None = None, upper_days: float | None = None):\n",
|
942
1174
|
" if not lower_days:\n",
|
943
1175
|
" lower_days = -1\n",
|
944
1176
|
" cutoff_datetime = df_visit.query(\"@lower_days<followup_days<=@upper_days\").visit_datetime.max()\n",
|
945
1177
|
" # exclude subjects for this reason\n",
|
946
|
-
" offstudy_reasons = [
|
1178
|
+
" offstudy_reasons = [\n",
|
1179
|
+
" 'Patient fulfilled late exclusion criteria (due to abnormal blood values or raised blood pressure at enrolment']\n",
|
947
1180
|
"\n",
|
948
1181
|
" df_eos = get_eos_df()\n",
|
949
1182
|
" df_eos_excluded = (\n",
|
@@ -954,7 +1187,8 @@
|
|
954
1187
|
" )\n",
|
955
1188
|
" df_visit_final = (\n",
|
956
1189
|
" df_visit.query(\"@lower_days<followup_days<=@upper_days and reason!='missed' and visit_code<2000.0\")\n",
|
957
|
-
" .merge(df_eos_excluded[[\"subject_identifier\"]], on=\"subject_identifier\", how=\"left\", suffixes=(\"\", \"_y\")
|
1190
|
+
" .merge(df_eos_excluded[[\"subject_identifier\"]], on=\"subject_identifier\", how=\"left\", suffixes=(\"\", \"_y\"),\n",
|
1191
|
+
" indicator=True)\n",
|
958
1192
|
" .query(\"_merge=='left_only'\")\n",
|
959
1193
|
" .drop(columns=[\"_merge\"])\n",
|
960
1194
|
" )\n",
|
@@ -968,35 +1202,39 @@
|
|
968
1202
|
" df_main = (\n",
|
969
1203
|
" df_main\n",
|
970
1204
|
" .merge(\n",
|
971
|
-
" df_endpoint.query(\"days_to_endpoint>@lower_days\")[
|
1205
|
+
" df_endpoint.query(\"days_to_endpoint>@lower_days\")[\n",
|
1206
|
+
" [\"subject_identifier\", \"endpoint_label\", \"endpoint_type\", \"days_to_endpoint\"]],\n",
|
972
1207
|
" how=\"left\",\n",
|
973
1208
|
" on=[\"subject_identifier\"])\n",
|
974
1209
|
" .reset_index(drop=True)\n",
|
975
1210
|
" )\n",
|
976
|
-
" if lower_days>=365.25:\n",
|
1211
|
+
" if lower_days >= 365.25:\n",
|
977
1212
|
" df_main[\"followup_days\"] = df_main[\"followup_days\"] - lower_days\n",
|
978
|
-
" df_main[\"followup_years\"] = df_main[\"followup_days\"]/365.25\n",
|
979
|
-
" return df_main, len(df_main), len(
|
1213
|
+
" df_main[\"followup_years\"] = df_main[\"followup_days\"] / 365.25\n",
|
1214
|
+
" return df_main, len(df_main), len(\n",
|
1215
|
+
" df_main.query(\"@lower_days<days_to_endpoint<=@upper_days and endpoint_label.notna()\"))\n",
|
1216
|
+
"\n",
|
980
1217
|
"\n",
|
981
1218
|
"def get_rate_and_ci(events, person_years_total):\n",
|
982
1219
|
" lower_ci = (chi2.ppf(0.025, 2 * events) / (2 * person_years_total)) * 1000\n",
|
983
1220
|
" upper_ci = (chi2.ppf(0.975, 2 * (events + 1)) / (2 * person_years_total)) * 1000\n",
|
984
|
-
" return events/person_years_total*1000, lower_ci, upper_ci\n",
|
1221
|
+
" return events / person_years_total * 1000, lower_ci, upper_ci\n",
|
1222
|
+
"\n",
|
985
1223
|
"\n",
|
986
|
-
"def get_incidence_data(term:str, lower_days:float, upper_days:float):\n",
|
1224
|
+
"def get_incidence_data(term: str, lower_days: float, upper_days: float):\n",
|
987
1225
|
" data = {}\n",
|
988
1226
|
" df_main, subjects, events = get_df_main(df_visit, lower_days=lower_days, upper_days=upper_days)\n",
|
989
1227
|
" person_years_total = df_main.followup_years.sum()\n",
|
990
|
-
" data.update({term:[person_years_total, subjects, events, *get_rate_and_ci(events, person_years_total)]})\n",
|
1228
|
+
" data.update({term: [person_years_total, subjects, events, *get_rate_and_ci(events, person_years_total)]})\n",
|
991
1229
|
" return data"
|
992
|
-
]
|
993
|
-
"id": "920db81ad440edab",
|
994
|
-
"outputs": [],
|
995
|
-
"execution_count": null
|
1230
|
+
]
|
996
1231
|
},
|
997
1232
|
{
|
998
|
-
"metadata": {},
|
999
1233
|
"cell_type": "code",
|
1234
|
+
"execution_count": null,
|
1235
|
+
"id": "32",
|
1236
|
+
"metadata": {},
|
1237
|
+
"outputs": [],
|
1000
1238
|
"source": [
|
1001
1239
|
"incidence_data = {}\n",
|
1002
1240
|
"incidence_data.update(get_incidence_data(\"total\", lower_days=-1, upper_days=10000))\n",
|
@@ -1016,49 +1254,51 @@
|
|
1016
1254
|
" data[\"lower_ci\"].append(v[4])\n",
|
1017
1255
|
" data[\"upper_ci\"].append(v[5])\n",
|
1018
1256
|
"\n",
|
1019
|
-
"df_table9 = pd.DataFrame(data={k:v for k,v in data.items() if k
|
1020
|
-
]
|
1021
|
-
"id": "44651e865641b75d",
|
1022
|
-
"outputs": [],
|
1023
|
-
"execution_count": null
|
1257
|
+
"df_table9 = pd.DataFrame(data={k: v for k, v in data.items() if k != \"subjects\"})"
|
1258
|
+
]
|
1024
1259
|
},
|
1025
1260
|
{
|
1026
|
-
"metadata": {},
|
1027
1261
|
"cell_type": "code",
|
1262
|
+
"execution_count": null,
|
1263
|
+
"id": "33",
|
1264
|
+
"metadata": {},
|
1265
|
+
"outputs": [],
|
1028
1266
|
"source": [
|
1029
1267
|
"gt = df_as_great_table(\n",
|
1030
1268
|
" df_table9,\n",
|
1031
|
-
" title=\"Table
|
1269
|
+
" title=\"Table 10: Incident Rate per 1000 person years\",\n",
|
1032
1270
|
" subtitle=md(\"using randomisation to diabetes/last seen\"),\n",
|
1033
1271
|
")\n",
|
1034
1272
|
"gt = gt.fmt_number(columns=[\"person_years\", \"failures\", \"rate\", \"lower_ci\", \"upper_ci\"], decimals=2)\n",
|
1035
1273
|
"gt = (gt\n",
|
1036
|
-
"
|
1037
|
-
"
|
1038
|
-
"
|
1039
|
-
"
|
1040
|
-
"
|
1041
|
-
"
|
1042
|
-
"
|
1043
|
-
"
|
1274
|
+
" .cols_label(\n",
|
1275
|
+
" {\"label\": \"Label\", \"person_years\": \"Person years\", \"failures\": \"Failures\", \"rate\": \"Rate\", \"lower_ci\": \"Lower\",\n",
|
1276
|
+
" \"upper_ci\": \"Upper\"})\n",
|
1277
|
+
" .cols_align(align=\"left\", columns=[\"label\"])\n",
|
1278
|
+
" .cols_align(align=\"center\", columns=[\"person_years\", \"failures\", \"rate\", \"lower_ci\", \"upper_ci\"])\n",
|
1279
|
+
" .tab_spanner(\n",
|
1280
|
+
" label=\"95%CI\",\n",
|
1281
|
+
" columns=[\"lower_ci\", \"upper_ci\"],\n",
|
1044
1282
|
")\n",
|
1283
|
+
" .tab_source_note(source_note=\"Excluding patients withdrawn for `late exclusion` criteria\")\n",
|
1284
|
+
" )\n",
|
1045
1285
|
"gt.show()\n",
|
1046
1286
|
"html_data.append(gt.as_raw_html())"
|
1047
|
-
]
|
1048
|
-
"id": "da4e67d83522768a",
|
1049
|
-
"outputs": [],
|
1050
|
-
"execution_count": null
|
1287
|
+
]
|
1051
1288
|
},
|
1052
1289
|
{
|
1053
|
-
"metadata": {},
|
1054
1290
|
"cell_type": "code",
|
1291
|
+
"execution_count": null,
|
1292
|
+
"id": "34",
|
1293
|
+
"metadata": {},
|
1294
|
+
"outputs": [],
|
1055
1295
|
"source": [
|
1056
|
-
"# Table
|
1296
|
+
"# Table 11: Proportion meeting primary endpoint\n",
|
1057
1297
|
"df_table10 = pd.DataFrame(data=data)\n",
|
1058
|
-
"df_table10[\"proportion\"] = df_table10[\"failures\"]/df_table10[\"subjects\"]*100\n",
|
1298
|
+
"df_table10[\"proportion\"] = df_table10[\"failures\"] / df_table10[\"subjects\"] * 100\n",
|
1059
1299
|
"gt = df_as_great_table(\n",
|
1060
1300
|
" df_table10[[\"label\", \"subjects\", 'failures', \"proportion\"]],\n",
|
1061
|
-
" title=\"Table
|
1301
|
+
" title=\"Table 11: Proportion meeting primary endpoint\",\n",
|
1062
1302
|
")\n",
|
1063
1303
|
"gt = (\n",
|
1064
1304
|
" gt\n",
|
@@ -1070,22 +1310,22 @@
|
|
1070
1310
|
")\n",
|
1071
1311
|
"html_data.append(gt.as_raw_html())\n",
|
1072
1312
|
"gt.show()\n"
|
1073
|
-
]
|
1074
|
-
"id": "2376a30803fbc743",
|
1075
|
-
"outputs": [],
|
1076
|
-
"execution_count": null
|
1313
|
+
]
|
1077
1314
|
},
|
1078
1315
|
{
|
1079
|
-
"metadata": {},
|
1080
1316
|
"cell_type": "code",
|
1081
|
-
"
|
1082
|
-
"id": "
|
1317
|
+
"execution_count": null,
|
1318
|
+
"id": "35",
|
1319
|
+
"metadata": {},
|
1083
1320
|
"outputs": [],
|
1084
|
-
"
|
1321
|
+
"source": []
|
1085
1322
|
},
|
1086
1323
|
{
|
1087
|
-
"metadata": {},
|
1088
1324
|
"cell_type": "code",
|
1325
|
+
"execution_count": null,
|
1326
|
+
"id": "36",
|
1327
|
+
"metadata": {},
|
1328
|
+
"outputs": [],
|
1089
1329
|
"source": [
|
1090
1330
|
"# Table 11a: End of Study Table (for those who have completed an end of study form)\n",
|
1091
1331
|
"df_eos = get_eos_df()\n",
|
@@ -1102,11 +1342,12 @@
|
|
1102
1342
|
" \"Patient withdrew consent to participate further\": \"Withdrawal: Consent\",\n",
|
1103
1343
|
"}\n",
|
1104
1344
|
"df_eos[\"offstudy_reason\"] = df_eos[\"offstudy_reason\"].map(offstudy_reasons)\n",
|
1105
|
-
"df_eos[\"offstudy_reason\"] = pd.Categorical(df_eos[\"offstudy_reason\"]
|
1345
|
+
"df_eos[\"offstudy_reason\"] = pd.Categorical(df_eos[\"offstudy_reason\"],\n",
|
1346
|
+
" categories=sorted(list(offstudy_reasons.values())), ordered=True)\n",
|
1106
1347
|
"df_eos[\"site_id\"] = df_eos[\"site_id\"].astype(str)\n",
|
1107
1348
|
"df_eos_pivot = (\n",
|
1108
1349
|
" df_eos\n",
|
1109
|
-
" .groupby(by=[\"offstudy_reason\", \"site_id\"],observed=True)\n",
|
1350
|
+
" .groupby(by=[\"offstudy_reason\", \"site_id\"], observed=True)\n",
|
1110
1351
|
" .size()\n",
|
1111
1352
|
" .reset_index()\n",
|
1112
1353
|
" .pivot_table(index=\"offstudy_reason\", columns=\"site_id\", values=0, observed=True)\n",
|
@@ -1114,43 +1355,44 @@
|
|
1114
1355
|
" .astype(int)\n",
|
1115
1356
|
" .reset_index()\n",
|
1116
1357
|
")\n",
|
1117
|
-
"df_eos_pivot[\"total\"] = df_eos_pivot[[\"10\", \"20\"
|
1118
|
-
"df_eos_pivot.columns.name
|
1358
|
+
"df_eos_pivot[\"total\"] = df_eos_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\"]].sum(axis=1)\n",
|
1359
|
+
"df_eos_pivot.columns.name = \"\"\n",
|
1119
1360
|
"sum_row = df_eos_pivot.select_dtypes(include='int64').sum()\n",
|
1120
1361
|
"sum_row['offstudy_reason'] = 'Total'\n",
|
1121
1362
|
"sum_row_df = pd.DataFrame(sum_row).T\n",
|
1122
|
-
"
|
1123
|
-
"
|
1124
|
-
"df_eos_pivot = pd.concat([
|
1363
|
+
"enrolled_1691_pivot[\"offstudy_reason\"] = \"Enrolled\"\n",
|
1364
|
+
"enrolled_1691_pivot = enrolled_1691_pivot[[*df_eos_pivot.columns]]\n",
|
1365
|
+
"df_eos_pivot = pd.concat([enrolled_1691_pivot, df_eos_pivot, sum_row_df], ignore_index=True)\n",
|
1125
1366
|
"\n",
|
1126
1367
|
"gt = df_as_great_table(\n",
|
1127
1368
|
" df_eos_pivot,\n",
|
1128
|
-
" title=\"Table
|
1369
|
+
" title=\"Table 12a: End of study report\",\n",
|
1129
1370
|
" subtitle=md(\"for those who have completed an End of study report\"),\n",
|
1130
1371
|
")\n",
|
1131
1372
|
"gt = (\n",
|
1132
1373
|
" gt\n",
|
1133
|
-
" .cols_label(
|
1374
|
+
" .cols_label(\n",
|
1375
|
+
" {\"offstudy_reason\": \"Reason\", **{k: v for k, v in column_headers.items() if k not in [\"visit_code\", \"label\"]}})\n",
|
1134
1376
|
" .cols_align(align=\"left\", columns=[\"offstudy_reason\"])\n",
|
1135
|
-
" .cols_align(align=\"center\", columns=[\"10\", \"20\"
|
1377
|
+
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
1136
1378
|
" .tab_style(\n",
|
1137
1379
|
" style=[style.fill(color=\"snow\"), style.text(color=\"black\")],\n",
|
1138
1380
|
" locations=loc.body(\n",
|
1139
1381
|
" columns=[0],\n",
|
1140
|
-
" rows=[len(df_eos_pivot)-1]),\n",
|
1141
|
-
"
|
1382
|
+
" rows=[len(df_eos_pivot) - 1]),\n",
|
1383
|
+
" )\n",
|
1142
1384
|
" .tab_style(\n",
|
1143
1385
|
" style=[style.fill(color=\"lightblue\"), style.text(color=\"black\")],\n",
|
1144
1386
|
" locations=loc.body(\n",
|
1145
1387
|
" columns=[\"10\", \"20\", \"30\", \"40\", \"60\"],\n",
|
1146
|
-
" rows=[len(df_eos_pivot)-1],\n",
|
1388
|
+
" rows=[len(df_eos_pivot) - 1],\n",
|
1147
1389
|
" ),\n",
|
1148
1390
|
" )\n",
|
1149
1391
|
" .tab_style(\n",
|
1150
1392
|
" style=[style.fill(color=\"lightgreen\"), style.text(color=\"black\")],\n",
|
1151
1393
|
" locations=loc.body(\n",
|
1152
1394
|
" columns=[\"total\"],\n",
|
1153
|
-
" rows=[len(df_eos_pivot)-1],\n",
|
1395
|
+
" rows=[len(df_eos_pivot) - 1],\n",
|
1154
1396
|
" ),\n",
|
1155
1397
|
" )\n",
|
1156
1398
|
" .tab_style(\n",
|
@@ -1163,41 +1405,44 @@
|
|
1163
1405
|
")\n",
|
1164
1406
|
"html_data.append(gt.as_raw_html())\n",
|
1165
1407
|
"gt.show()\n"
|
1166
|
-
]
|
1167
|
-
"id": "37dcd320411bd9c5",
|
1168
|
-
"outputs": [],
|
1169
|
-
"execution_count": null
|
1408
|
+
]
|
1170
1409
|
},
|
1171
1410
|
{
|
1172
|
-
"metadata": {},
|
1173
1411
|
"cell_type": "code",
|
1174
|
-
"
|
1175
|
-
"id": "
|
1412
|
+
"execution_count": null,
|
1413
|
+
"id": "37",
|
1414
|
+
"metadata": {},
|
1176
1415
|
"outputs": [],
|
1177
|
-
"
|
1416
|
+
"source": []
|
1178
1417
|
},
|
1179
1418
|
{
|
1180
|
-
"metadata": {},
|
1181
1419
|
"cell_type": "code",
|
1420
|
+
"execution_count": null,
|
1421
|
+
"id": "38",
|
1422
|
+
"metadata": {},
|
1423
|
+
"outputs": [],
|
1182
1424
|
"source": [
|
1183
|
-
"# Table
|
1184
|
-
"def get_schedule_df(df_subjecthistory:pd.DataFrame, onschedule_model:str, offschedule_model:
|
1185
|
-
"
|
1425
|
+
"# Table 12b: Study status\n",
|
1426
|
+
"def get_schedule_df(df_subjecthistory: pd.DataFrame, onschedule_model: str, offschedule_model: str,\n",
|
1427
|
+
" mode: str) -> pd.DataFrame:\n",
|
1428
|
+
" columns = {k: f\"{k}_{mode}\" for k in [\"10\", \"20\", \"30\", \"40\", \"60\"]}\n",
|
1186
1429
|
" df_schedule = (\n",
|
1187
1430
|
" df_subjecthistory\n",
|
1188
|
-
" .query(
|
1431
|
+
" .query(\n",
|
1432
|
+
" f\"onschedule_model==@onschedule_model and offschedule_model==@offschedule_model and offschedule_datetime.{'isna' if mode == 'on' else 'notna'}()\")\n",
|
1189
1433
|
" .groupby(by=[\"onschedule_model\", \"site_id\"])\n",
|
1190
1434
|
" .size()\n",
|
1191
1435
|
" .reset_index()\n",
|
1192
1436
|
" .pivot_table(index=\"onschedule_model\", columns=\"site_id\", values=0, observed=True)\n",
|
1193
1437
|
" .reset_index()\n",
|
1194
|
-
" .rename(columns={\"onschedule_model\"
|
1438
|
+
" .rename(columns={\"onschedule_model\": \"schedule\", **columns})\n",
|
1195
1439
|
" .fillna(0)\n",
|
1196
1440
|
" .copy()\n",
|
1197
1441
|
" )\n",
|
1198
1442
|
" df_schedule.columns.name = \"\"\n",
|
1199
1443
|
" return df_schedule\n",
|
1200
1444
|
"\n",
|
1445
|
+
"\n",
|
1201
1446
|
"df_subjecthistory = read_frame(SubjectScheduleHistory.objects.all(), verbose=False).rename(columns={\"site\": \"site_id\"})\n",
|
1202
1447
|
"df_subjecthistory[\"site_id\"] = df_subjecthistory[\"site_id\"].astype(str)\n",
|
1203
1448
|
"\n",
|
@@ -1232,179 +1477,209 @@
|
|
1232
1477
|
"df_status[\"total_on\"] = df_status[[col for col in columns if \"on\" in col]].sum(axis=1)\n",
|
1233
1478
|
"df_status[\"total_off\"] = df_status[[col for col in columns if \"off\" in col]].sum(axis=1)\n",
|
1234
1479
|
"df_status[\"total\"] = df_status[columns].sum(axis=1)\n",
|
1235
|
-
"df_status[\"schedule\"] = df_status.schedule.map(
|
1480
|
+
"df_status[\"schedule\"] = df_status.schedule.map(\n",
|
1481
|
+
" {\"meta_prn.onschedule\": \"Main trial\", \"meta_prn.onscheduledmreferral\": \"Diabetes\",\n",
|
1482
|
+
" \"meta_prn.onschedulepregnancy\": \"Pregnancy\"})\n",
|
1236
1483
|
"\n",
|
1237
1484
|
"gt = df_as_great_table(\n",
|
1238
1485
|
" df_status,\n",
|
1239
|
-
" title=\"Table
|
1486
|
+
" title=\"Table 12b: Study status\",\n",
|
1240
1487
|
" subtitle=md(\"Calculated from Offschedule form; not End of study report\"),\n",
|
1241
1488
|
")\n",
|
1242
1489
|
"# gt = gt.fmt_number(columns=[\"person_years\", \"failures\", \"rate\", \"lower_ci\", \"upper_ci\"], decimals=0)\n",
|
1243
1490
|
"gt = (gt\n",
|
1244
|
-
"
|
1245
|
-
"
|
1246
|
-
"
|
1247
|
-
"
|
1248
|
-
"
|
1249
|
-
" )\n",
|
1491
|
+
" .tab_source_note(\n",
|
1492
|
+
" source_note=(\n",
|
1493
|
+
" \"Note: Offschedule form is always submitted before the End of study report. \"\n",
|
1494
|
+
" \"When the Offschedule form is submitted, future appointments for the schedule are removed and \"\n",
|
1495
|
+
" \"the site staff are actioned to submit the End of study report.\"\n",
|
1250
1496
|
" )\n",
|
1251
|
-
" .cols_label({\n",
|
1252
|
-
" \"10_on\": \"On\", \"10_off\": \"Off\",\n",
|
1253
|
-
" \"20_on\": \"On\", \"20_off\": \"Off\",\n",
|
1254
|
-
" \"30_on\": \"On\", \"30_off\": \"Off\",\n",
|
1255
|
-
" \"40_on\": \"On\", \"40_off\": \"Off\",\n",
|
1256
|
-
" \"60_on\": \"On\", \"60_off\": \"Off\",\n",
|
1257
|
-
" \"total_on\": \"On\", \"total_off\": \"Off\",\n",
|
1258
|
-
" \"schedule\": \"Schedule\", \"total\": \"Total\"})\n",
|
1259
|
-
" .cols_align(align=\"center\")\n",
|
1260
|
-
" .cols_align(align=\"left\", columns=[\"label\"])\n",
|
1261
|
-
" .tab_spanner(\n",
|
1262
|
-
" label=\"Hindu mandal\",\n",
|
1263
|
-
" columns=[\"10_on\", \"10_off\"],\n",
|
1264
|
-
" )\n",
|
1265
|
-
" .tab_spanner(\n",
|
1266
|
-
" label=\"Amana\",\n",
|
1267
|
-
" columns=[\"20_on\", \"20_off\"],\n",
|
1268
|
-
" )\n",
|
1269
|
-
" .tab_spanner(\n",
|
1270
|
-
" label=\"Temeke\",\n",
|
1271
|
-
" columns=[\"30_on\", \"30_off\"],\n",
|
1272
|
-
" )\n",
|
1273
|
-
" .tab_spanner(\n",
|
1274
|
-
" label=\"Mwananyamala\",\n",
|
1275
|
-
" columns=[\"40_on\", \"40_off\"],\n",
|
1276
|
-
" )\n",
|
1277
|
-
" .tab_spanner(\n",
|
1278
|
-
" label=\"Mnazi Moja\",\n",
|
1279
|
-
" columns=[\"60_on\", \"60_off\"],\n",
|
1280
|
-
" )\n",
|
1281
|
-
" .tab_spanner(\n",
|
1282
|
-
" label=\"Total\",\n",
|
1283
|
-
" columns=[\"total_on\", \"total_off\"],\n",
|
1284
|
-
" )\n",
|
1285
|
-
" .tab_style(\n",
|
1286
|
-
" style=[style.fill(color=\"lightblue\"), style.text(color=\"black\")],\n",
|
1287
|
-
" locations=loc.body(\n",
|
1288
|
-
" columns=[\"10_off\", \"20_off\", \"30_off\", \"40_off\", \"60_off\"],\n",
|
1289
|
-
" rows=list(range(0, 1)),\n",
|
1290
|
-
" ),\n",
|
1291
|
-
" )\n",
|
1292
|
-
" .tab_style(\n",
|
1293
|
-
" style=[style.fill(color=\"lightgreen\"), style.text(color=\"black\")],\n",
|
1294
|
-
" locations=loc.body(\n",
|
1295
|
-
" columns=[\"total_off\"],\n",
|
1296
|
-
" rows=list(range(0, 1)),\n",
|
1297
|
-
" ),\n",
|
1298
|
-
" )\n",
|
1299
|
-
" .fmt_number(columns=[*[c for c in df_status.columns if c not in [\"schedule\"]]], decimals=0)\n",
|
1300
1497
|
")\n",
|
1498
|
+
" .cols_label({\n",
|
1499
|
+
" \"10_on\": \"On\", \"10_off\": \"Off\",\n",
|
1500
|
+
" \"20_on\": \"On\", \"20_off\": \"Off\",\n",
|
1501
|
+
" \"30_on\": \"On\", \"30_off\": \"Off\",\n",
|
1502
|
+
" \"40_on\": \"On\", \"40_off\": \"Off\",\n",
|
1503
|
+
" \"60_on\": \"On\", \"60_off\": \"Off\",\n",
|
1504
|
+
" \"total_on\": \"On\", \"total_off\": \"Off\",\n",
|
1505
|
+
" \"schedule\": \"Schedule\", \"total\": \"Total\"})\n",
|
1506
|
+
" .cols_align(align=\"center\")\n",
|
1507
|
+
" .cols_align(align=\"left\", columns=[\"label\"])\n",
|
1508
|
+
" .tab_spanner(\n",
|
1509
|
+
" label=\"Hindu mandal\",\n",
|
1510
|
+
" columns=[\"10_on\", \"10_off\"],\n",
|
1511
|
+
")\n",
|
1512
|
+
" .tab_spanner(\n",
|
1513
|
+
" label=\"Amana\",\n",
|
1514
|
+
" columns=[\"20_on\", \"20_off\"],\n",
|
1515
|
+
")\n",
|
1516
|
+
" .tab_spanner(\n",
|
1517
|
+
" label=\"Temeke\",\n",
|
1518
|
+
" columns=[\"30_on\", \"30_off\"],\n",
|
1519
|
+
")\n",
|
1520
|
+
" .tab_spanner(\n",
|
1521
|
+
" label=\"Mwananyamala\",\n",
|
1522
|
+
" columns=[\"40_on\", \"40_off\"],\n",
|
1523
|
+
")\n",
|
1524
|
+
" .tab_spanner(\n",
|
1525
|
+
" label=\"Mnazi Moja\",\n",
|
1526
|
+
" columns=[\"60_on\", \"60_off\"],\n",
|
1527
|
+
")\n",
|
1528
|
+
" .tab_spanner(\n",
|
1529
|
+
" label=\"Total\",\n",
|
1530
|
+
" columns=[\"total_on\", \"total_off\"],\n",
|
1531
|
+
")\n",
|
1532
|
+
" .tab_style(\n",
|
1533
|
+
" style=[style.fill(color=\"lightblue\"), style.text(color=\"black\")],\n",
|
1534
|
+
" locations=loc.body(\n",
|
1535
|
+
" columns=[\"10_off\", \"20_off\", \"30_off\", \"40_off\", \"60_off\"],\n",
|
1536
|
+
" rows=list(range(0, 1)),\n",
|
1537
|
+
" ),\n",
|
1538
|
+
")\n",
|
1539
|
+
" .tab_style(\n",
|
1540
|
+
" style=[style.fill(color=\"lightgreen\"), style.text(color=\"black\")],\n",
|
1541
|
+
" locations=loc.body(\n",
|
1542
|
+
" columns=[\"total_off\"],\n",
|
1543
|
+
" rows=list(range(0, 1)),\n",
|
1544
|
+
" ),\n",
|
1545
|
+
")\n",
|
1546
|
+
" .fmt_number(columns=[*[c for c in df_status.columns if c not in [\"schedule\"]]], decimals=0)\n",
|
1547
|
+
" )\n",
|
1301
1548
|
"html_data.append(gt.as_raw_html())\n",
|
1302
1549
|
"gt.show()"
|
1303
|
-
]
|
1304
|
-
|
1550
|
+
]
|
1551
|
+
},
|
1552
|
+
{
|
1553
|
+
"cell_type": "code",
|
1554
|
+
"execution_count": null,
|
1555
|
+
"id": "39",
|
1556
|
+
"metadata": {},
|
1305
1557
|
"outputs": [],
|
1306
|
-
"
|
1558
|
+
"source": [
|
1559
|
+
"# off schedule no eos\n",
|
1560
|
+
"\n",
|
1561
|
+
"subjects_preg_dm = df_subjecthistory[~(df_subjecthistory.offschedule_datetime.isna()) & (\n",
|
1562
|
+
" df_subjecthistory.schedule_name != \"schedule\")].subject_identifier\n",
|
1563
|
+
"\n",
|
1564
|
+
"df_subjecthistory[\n",
|
1565
|
+
" ~(df_subjecthistory.subject_identifier.isin(df_eos_1691.subject_identifier))].sort_values(\n",
|
1566
|
+
" by=[\"subject_identifier\", \"onschedule_datetime\"])"
|
1567
|
+
]
|
1307
1568
|
},
|
1308
1569
|
{
|
1570
|
+
"cell_type": "code",
|
1571
|
+
"execution_count": null,
|
1572
|
+
"id": "40",
|
1309
1573
|
"metadata": {},
|
1574
|
+
"outputs": [],
|
1575
|
+
"source": []
|
1576
|
+
},
|
1577
|
+
{
|
1310
1578
|
"cell_type": "code",
|
1579
|
+
"execution_count": null,
|
1580
|
+
"id": "41",
|
1581
|
+
"metadata": {},
|
1582
|
+
"outputs": [],
|
1311
1583
|
"source": [
|
1312
|
-
"# Table
|
1584
|
+
"# Table 13: Loss to Follow Up\n",
|
1313
1585
|
"df_ltfu = read_frame(LossToFollowup.objects.all(), verbose=False).rename(columns={\"site\": \"site_id\"})\n",
|
1314
1586
|
"df_ltfu_pivot = (\n",
|
1315
1587
|
" df_ltfu\n",
|
1316
|
-
" .groupby(by=[\"loss_category\", \"site_id\"],observed=True,dropna=False)\n",
|
1588
|
+
" .groupby(by=[\"loss_category\", \"site_id\"], observed=True, dropna=False)\n",
|
1317
1589
|
" .size()\n",
|
1318
1590
|
" .reset_index()\n",
|
1319
|
-
" .pivot_table(index=\"loss_category\", columns=\"site_id\", values=0, observed=True,dropna=False)\n",
|
1591
|
+
" .pivot_table(index=\"loss_category\", columns=\"site_id\", values=0, observed=True, dropna=False)\n",
|
1320
1592
|
" .fillna(0)\n",
|
1321
1593
|
" .astype(int)\n",
|
1322
1594
|
" .reset_index()\n",
|
1323
1595
|
")\n",
|
1324
|
-
"df_ltfu_pivot[\"total\"] = df_eos_pivot[[\"10\", \"20\"
|
1325
|
-
"df_ltfu_pivot.columns.name
|
1596
|
+
"df_ltfu_pivot[\"total\"] = df_eos_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\"]].sum(axis=1)\n",
|
1597
|
+
"df_ltfu_pivot.columns.name = \"\"\n",
|
1326
1598
|
"sum_row = df_ltfu_pivot.select_dtypes(include='int64').sum()\n",
|
1327
1599
|
"sum_row['loss_category'] = 'Total'\n",
|
1328
1600
|
"sum_row_df = pd.DataFrame(sum_row).T\n",
|
1329
1601
|
"df_ltfu_pivot = pd.concat([df_ltfu_pivot, sum_row_df], ignore_index=True)\n",
|
1330
1602
|
"df_ltfu_pivot\n"
|
1331
|
-
]
|
1332
|
-
"id": "534c51e7321e2ef3",
|
1333
|
-
"outputs": [],
|
1334
|
-
"execution_count": null
|
1603
|
+
]
|
1335
1604
|
},
|
1336
1605
|
{
|
1337
|
-
"metadata": {},
|
1338
1606
|
"cell_type": "code",
|
1607
|
+
"execution_count": null,
|
1608
|
+
"id": "42",
|
1609
|
+
"metadata": {},
|
1610
|
+
"outputs": [],
|
1339
1611
|
"source": [
|
1340
|
-
"# Table
|
1612
|
+
"# Table 13c: End of study report not submitted\n",
|
1341
1613
|
"\n",
|
1342
1614
|
"df1 = (\n",
|
1343
1615
|
" df_status\n",
|
1344
1616
|
" .query(\"schedule=='Main trial'\")[[col for col in columns if \"off\" in col]]\n",
|
1345
|
-
" .rename(columns=dict(zip([col for col in columns if \"off\" in col], [\"10\", \"20\"
|
1617
|
+
" .rename(columns=dict(zip([col for col in columns if \"off\" in col], [\"10\", \"20\", \"30\", \"40\", \"60\"])))\n",
|
1346
1618
|
" .reset_index(drop=True)\n",
|
1347
1619
|
")\n",
|
1348
1620
|
"df2 = (\n",
|
1349
1621
|
" df_eos_pivot\n",
|
1350
|
-
" .query(\"offstudy_reason=='Total'\")[[\"10\", \"20\"
|
1622
|
+
" .query(\"offstudy_reason=='Total'\")[[\"10\", \"20\", \"30\", \"40\", \"60\"]]\n",
|
1351
1623
|
" .reset_index(drop=True)\n",
|
1352
1624
|
")\n",
|
1353
1625
|
"\n",
|
1354
|
-
"df_eos_not_reported = df1-df2\n",
|
1626
|
+
"df_eos_not_reported = df1 - df2\n",
|
1355
1627
|
"df_eos_not_reported[\"schedule\"] = 'Main trial'\n",
|
1356
|
-
"df_eos_not_reported[\"total\"] = df_eos_not_reported[[\"10\", \"20\"
|
1357
|
-
"df_eos_not_reported = df_eos_not_reported[[\"schedule\", \"10\", \"20\"
|
1628
|
+
"df_eos_not_reported[\"total\"] = df_eos_not_reported[[\"10\", \"20\", \"30\", \"40\", \"60\"]].sum(axis=1)\n",
|
1629
|
+
"df_eos_not_reported = df_eos_not_reported[[\"schedule\", \"10\", \"20\", \"30\", \"40\", \"60\", \"total\"]]\n",
|
1358
1630
|
"\n",
|
1359
1631
|
"gt = df_as_great_table(\n",
|
1360
1632
|
" df_eos_not_reported,\n",
|
1361
|
-
" title=\"Table
|
1633
|
+
" title=\"Table 13c: End of study report not submitted\",\n",
|
1362
1634
|
" subtitle=md(\"End of study report expected based on Offschedule form\"),\n",
|
1363
1635
|
")\n",
|
1364
1636
|
"gt = (\n",
|
1365
1637
|
" gt\n",
|
1366
|
-
" .cols_label(
|
1638
|
+
" .cols_label(\n",
|
1639
|
+
" {\"schedule\": \"Schedule\", **{k: v for k, v in column_headers.items() if k not in [\"visit_code\", \"label\"]}})\n",
|
1367
1640
|
" .cols_align(align=\"left\", columns=[\"schedule\"])\n",
|
1368
|
-
" .cols_align(align=\"center\", columns=[\"10\", \"20\"
|
1641
|
+
" .cols_align(align=\"center\", columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
|
1369
1642
|
" .tab_style(\n",
|
1370
1643
|
" style=[style.fill(color=\"snow\"), style.text(color=\"black\")],\n",
|
1371
1644
|
" locations=loc.body(\n",
|
1372
1645
|
" columns=[0],\n",
|
1373
|
-
" rows=[len(df_eos_pivot)-1]),\n",
|
1374
|
-
"
|
1646
|
+
" rows=[len(df_eos_pivot) - 1]),\n",
|
1647
|
+
" )\n",
|
1375
1648
|
" .tab_style(\n",
|
1376
1649
|
" style=[style.fill(color=\"lightblue\"), style.text(color=\"black\")],\n",
|
1377
1650
|
" locations=loc.body(\n",
|
1378
1651
|
" columns=[\"10\", \"20\", \"30\", \"40\", \"60\"],\n",
|
1379
|
-
" rows=[len(df_eos_pivot)-1],\n",
|
1652
|
+
" rows=[len(df_eos_pivot) - 1],\n",
|
1380
1653
|
" ),\n",
|
1381
1654
|
" )\n",
|
1382
1655
|
" .tab_style(\n",
|
1383
1656
|
" style=[style.fill(color=\"lightgreen\"), style.text(color=\"black\")],\n",
|
1384
1657
|
" locations=loc.body(\n",
|
1385
1658
|
" columns=[\"total\"],\n",
|
1386
|
-
" rows=[len(df_eos_pivot)-1],\n",
|
1659
|
+
" rows=[len(df_eos_pivot) - 1],\n",
|
1387
1660
|
" ),\n",
|
1388
1661
|
" )\n",
|
1389
1662
|
")\n",
|
1390
1663
|
"html_data.append(gt.as_raw_html())\n",
|
1391
1664
|
"gt.show()\n"
|
1392
|
-
]
|
1393
|
-
"id": "25d05831ef76f267",
|
1394
|
-
"outputs": [],
|
1395
|
-
"execution_count": null
|
1665
|
+
]
|
1396
1666
|
},
|
1397
1667
|
{
|
1398
|
-
"metadata": {},
|
1399
1668
|
"cell_type": "code",
|
1400
|
-
"
|
1401
|
-
"id": "
|
1669
|
+
"execution_count": null,
|
1670
|
+
"id": "43",
|
1671
|
+
"metadata": {},
|
1402
1672
|
"outputs": [],
|
1403
|
-
"
|
1673
|
+
"source": [
|
1674
|
+
"# Table 14: Baseline Sample"
|
1675
|
+
]
|
1404
1676
|
},
|
1405
1677
|
{
|
1406
|
-
"metadata": {},
|
1407
1678
|
"cell_type": "code",
|
1679
|
+
"execution_count": null,
|
1680
|
+
"id": "44",
|
1681
|
+
"metadata": {},
|
1682
|
+
"outputs": [],
|
1408
1683
|
"source": [
|
1409
1684
|
"# Table 15: Consented to extended followup\n",
|
1410
1685
|
"df_consented = (\n",
|
@@ -1430,16 +1705,16 @@
|
|
1430
1705
|
")\n",
|
1431
1706
|
"if \"60\" not in df_consented_pivot.columns:\n",
|
1432
1707
|
" df_consented_pivot[\"60\"] = 0.0 * len(df_consented_pivot)\n",
|
1433
|
-
"df_consented_pivot.columns.name
|
1708
|
+
"df_consented_pivot.columns.name = \"\"\n",
|
1434
1709
|
"df_consented_pivot[\"year\"] = df_consented_pivot[\"year\"].astype(str)\n",
|
1435
1710
|
"df_consented_pivot[\"month\"] = df_consented_pivot[\"month\"].astype(str)\n",
|
1436
1711
|
"\n",
|
1437
|
-
"sum_row = df_consented_pivot[[\"10\", \"20\"
|
1712
|
+
"sum_row = df_consented_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\"]].sum()\n",
|
1438
1713
|
"sum_row['year'] = \"Total\"\n",
|
1439
1714
|
"sum_row['month'] = \"\"\n",
|
1440
1715
|
"df_consented_pivot = pd.concat([df_consented_pivot, sum_row.to_frame().T], ignore_index=True)\n",
|
1441
|
-
"df_consented_pivot[\"total\"] = df_consented_pivot[[\"10\", \"20\"
|
1442
|
-
"df_consented_pivot[[\"10\", \"20\"
|
1716
|
+
"df_consented_pivot[\"total\"] = df_consented_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\"]].sum(axis=1).astype(int)\n",
|
1717
|
+
"df_consented_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\"]] = df_consented_pivot[[\"10\", \"20\", \"30\", \"40\", \"60\"]].astype(int)\n",
|
1443
1718
|
"gt = df_as_great_table2(\n",
|
1444
1719
|
" df_consented_pivot,\n",
|
1445
1720
|
" title=\"Table 15: Consented to extended followup\",\n",
|
@@ -1448,7 +1723,8 @@
|
|
1448
1723
|
")\n",
|
1449
1724
|
"gt = (\n",
|
1450
1725
|
" gt\n",
|
1451
|
-
" .cols_label({\"year\": \"Year\", \"month\": \"Month\"
|
1726
|
+
" .cols_label({\"year\": \"Year\", \"month\": \"Month\",\n",
|
1727
|
+
" **{k: v for k, v in column_headers.items() if k not in [\"visit_code\", \"label\"]}})\n",
|
1452
1728
|
" .cols_align(align=\"center\")\n",
|
1453
1729
|
" .fmt_number(columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"], decimals=0)\n",
|
1454
1730
|
" .tab_stubhead(label=\"Consented\")\n",
|
@@ -1462,22 +1738,22 @@
|
|
1462
1738
|
")\n",
|
1463
1739
|
"html_data.append(gt.as_raw_html())\n",
|
1464
1740
|
"gt.show()"
|
1465
|
-
]
|
1466
|
-
"id": "8acd2dd7e5a958e9",
|
1467
|
-
"outputs": [],
|
1468
|
-
"execution_count": null
|
1741
|
+
]
|
1469
1742
|
},
|
1470
1743
|
{
|
1471
|
-
"metadata": {},
|
1472
1744
|
"cell_type": "code",
|
1473
|
-
"
|
1474
|
-
"id": "
|
1745
|
+
"execution_count": null,
|
1746
|
+
"id": "45",
|
1747
|
+
"metadata": {},
|
1475
1748
|
"outputs": [],
|
1476
|
-
"
|
1749
|
+
"source": []
|
1477
1750
|
},
|
1478
1751
|
{
|
1479
|
-
"metadata": {},
|
1480
1752
|
"cell_type": "code",
|
1753
|
+
"execution_count": null,
|
1754
|
+
"id": "46",
|
1755
|
+
"metadata": {},
|
1756
|
+
"outputs": [],
|
1481
1757
|
"source": [
|
1482
1758
|
"# gather raw html\n",
|
1483
1759
|
"raw_html = [f'<div class=\"page-break\">{s}</div>' for s in html_data]\n",
|
@@ -1496,45 +1772,42 @@
|
|
1496
1772
|
"\"\"\"\n",
|
1497
1773
|
"raw_html = ''.join(raw_html)\n",
|
1498
1774
|
"raw_html = f'<!DOCTYPE html>\\n<html lang=\"en\">\\n{style_css}\\n<head>\\n<meta charset=\"utf-8\"/>\\n</head>\\n<body>\\n' + document_title + raw_html + '\\n</body>\\n</html>\\n'"
|
1499
|
-
]
|
1500
|
-
"id": "a38e9d7ba59d063b",
|
1501
|
-
"outputs": [],
|
1502
|
-
"execution_count": null
|
1775
|
+
]
|
1503
1776
|
},
|
1504
1777
|
{
|
1505
|
-
"metadata": {},
|
1506
1778
|
"cell_type": "code",
|
1779
|
+
"execution_count": null,
|
1780
|
+
"id": "47",
|
1781
|
+
"metadata": {},
|
1782
|
+
"outputs": [],
|
1507
1783
|
"source": [
|
1508
1784
|
"# render html to PDF\n",
|
1509
1785
|
"pdfkit.from_string(raw_html, str(analysis_folder / pdf_filename),\n",
|
1510
|
-
"options={\n",
|
1511
|
-
"
|
1512
|
-
"
|
1513
|
-
"
|
1514
|
-
"
|
1515
|
-
"
|
1516
|
-
"
|
1517
|
-
"
|
1518
|
-
"
|
1519
|
-
"
|
1520
|
-
"
|
1521
|
-
"
|
1522
|
-
"
|
1523
|
-
"
|
1524
|
-
"},\n",
|
1525
|
-
"verbose=True)"
|
1526
|
-
]
|
1527
|
-
"id": "792243aad557cc86",
|
1528
|
-
"outputs": [],
|
1529
|
-
"execution_count": null
|
1786
|
+
" options={\n",
|
1787
|
+
" 'footer-center': 'Page [page] of [topage]',\n",
|
1788
|
+
" 'footer-font-size': '8',\n",
|
1789
|
+
" 'footer-spacing': '5',\n",
|
1790
|
+
" 'encoding': \"UTF-8\",\n",
|
1791
|
+
" 'margin-top': '10mm',\n",
|
1792
|
+
" 'margin-right': '15mm',\n",
|
1793
|
+
" 'margin-bottom': '15mm',\n",
|
1794
|
+
" 'margin-left': '15mm',\n",
|
1795
|
+
" 'header-center': study_title,\n",
|
1796
|
+
" 'header-font-size': '6',\n",
|
1797
|
+
" 'header-spacing': '0',\n",
|
1798
|
+
" 'disable-javascript': None,\n",
|
1799
|
+
" 'no-outline': None,\n",
|
1800
|
+
" },\n",
|
1801
|
+
" verbose=True)"
|
1802
|
+
]
|
1530
1803
|
},
|
1531
1804
|
{
|
1532
|
-
"metadata": {},
|
1533
1805
|
"cell_type": "code",
|
1534
|
-
"
|
1535
|
-
"id": "
|
1806
|
+
"execution_count": null,
|
1807
|
+
"id": "48",
|
1808
|
+
"metadata": {},
|
1536
1809
|
"outputs": [],
|
1537
|
-
"
|
1810
|
+
"source": []
|
1538
1811
|
}
|
1539
1812
|
],
|
1540
1813
|
"metadata": {
|