cehrbert-data 0.0.6__tar.gz → 0.0.7__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.
Files changed (142) hide show
  1. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/PKG-INFO +3 -2
  2. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/apps/generate_included_concept_list.py +21 -3
  3. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/apps/generate_training_data.py +22 -1
  4. cehrbert_data-0.0.7/src/cehrbert_data/cohorts/atrial_fibrillation.py +63 -0
  5. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/cabg.py +13 -5
  6. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/coronary_artery_disease.py +15 -5
  7. cehrbert_data-0.0.7/src/cehrbert_data/cohorts/ischemic_stroke.py +61 -0
  8. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/last_visit_discharged_home.py +1 -1
  9. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/spark_app_base.py +62 -32
  10. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/type_two_diabietes.py +16 -7
  11. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/decorators/__init__.py +1 -0
  12. cehrbert_data-0.0.7/src/cehrbert_data/decorators/prediction_token_decorator.py +73 -0
  13. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/decorators/token_priority.py +1 -0
  14. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/copd_readmission.py +2 -2
  15. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/hf_readmission.py +2 -2
  16. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/hospitalization_mortality.py +1 -1
  17. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/tools/extract_features.py +54 -87
  18. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/utils/spark_parse_args.py +7 -0
  19. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/utils/spark_utils.py +182 -0
  20. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data.egg-info/PKG-INFO +3 -2
  21. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data.egg-info/SOURCES.txt +1 -0
  22. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/tests/integration_tests/test_hf_readmission.py +1 -0
  23. cehrbert_data-0.0.6/src/cehrbert_data/cohorts/atrial_fibrillation.py +0 -45
  24. cehrbert_data-0.0.6/src/cehrbert_data/cohorts/ischemic_stroke.py +0 -45
  25. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/.github/workflows/python-build.yml +0 -0
  26. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/.github/workflows/tests.yml +0 -0
  27. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/.gitignore +0 -0
  28. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/.pre-commit-config.yaml +0 -0
  29. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/LICENSE +0 -0
  30. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/README.md +0 -0
  31. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/pyproject.toml +0 -0
  32. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept/._SUCCESS.crc +0 -0
  33. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept/.part-00000-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet.crc +0 -0
  34. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept/.part-00003-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet.crc +0 -0
  35. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept/.part-00010-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet.crc +0 -0
  36. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept/_SUCCESS +0 -0
  37. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept/part-00000-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet +0 -0
  38. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept/part-00003-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet +0 -0
  39. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept/part-00010-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet +0 -0
  40. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/._SUCCESS.crc +0 -0
  41. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/.part-00000-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  42. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/.part-00002-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  43. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/.part-00006-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  44. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/.part-00011-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  45. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/.part-00013-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  46. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/_SUCCESS +0 -0
  47. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/part-00000-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  48. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/part-00002-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  49. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/part-00006-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  50. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/part-00011-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  51. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_ancestor/part-00013-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  52. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/._SUCCESS.crc +0 -0
  53. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/.part-00000-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet.crc +0 -0
  54. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/.part-00002-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet.crc +0 -0
  55. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/.part-00007-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet.crc +0 -0
  56. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/.part-00012-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet.crc +0 -0
  57. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/_SUCCESS +0 -0
  58. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/part-00000-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet +0 -0
  59. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/part-00002-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet +0 -0
  60. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/part-00007-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet +0 -0
  61. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/concept_relationship/part-00012-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet +0 -0
  62. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/condition_occurrence/._SUCCESS.crc +0 -0
  63. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/condition_occurrence/.part-00000-4eff03a1-cdcf-4c89-b0cd-9ce590b9b1eb-c000.snappy.parquet.crc +0 -0
  64. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/condition_occurrence/_SUCCESS +0 -0
  65. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/condition_occurrence/part-00000-4eff03a1-cdcf-4c89-b0cd-9ce590b9b1eb-c000.snappy.parquet +0 -0
  66. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/drug_exposure/._SUCCESS.crc +0 -0
  67. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/drug_exposure/.part-00000-10bbf1a4-a7da-416e-9703-58609c7edfad-c000.snappy.parquet.crc +0 -0
  68. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/drug_exposure/_SUCCESS +0 -0
  69. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/drug_exposure/part-00000-10bbf1a4-a7da-416e-9703-58609c7edfad-c000.snappy.parquet +0 -0
  70. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/observation_period/._SUCCESS.crc +0 -0
  71. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/observation_period/.part-00000-694316e5-cc95-49f1-9fad-5a7f377e2602-c000.snappy.parquet.crc +0 -0
  72. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/observation_period/_SUCCESS +0 -0
  73. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/observation_period/part-00000-694316e5-cc95-49f1-9fad-5a7f377e2602-c000.snappy.parquet +0 -0
  74. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/person/._SUCCESS.crc +0 -0
  75. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/person/.part-00000-7d789011-f361-48da-af6f-cfe102978b3a-c000.snappy.parquet.crc +0 -0
  76. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/person/_SUCCESS +0 -0
  77. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/person/part-00000-7d789011-f361-48da-af6f-cfe102978b3a-c000.snappy.parquet +0 -0
  78. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/procedure_occurrence/._SUCCESS.crc +0 -0
  79. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/procedure_occurrence/.part-00000-e73003c1-aed5-41c0-b2d4-eaccaccf044a-c000.snappy.parquet.crc +0 -0
  80. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/procedure_occurrence/_SUCCESS +0 -0
  81. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/procedure_occurrence/part-00000-e73003c1-aed5-41c0-b2d4-eaccaccf044a-c000.snappy.parquet +0 -0
  82. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/visit_occurrence/._SUCCESS.crc +0 -0
  83. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/visit_occurrence/.part-00000-e874b5f1-bf9e-4cb9-93bf-c309a47b0476-c000.snappy.parquet.crc +0 -0
  84. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/visit_occurrence/_SUCCESS +0 -0
  85. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/sample_data/omop_sample/visit_occurrence/part-00000-e874b5f1-bf9e-4cb9-93bf-c309a47b0476-c000.snappy.parquet +0 -0
  86. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/setup.cfg +0 -0
  87. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/__init__.py +0 -0
  88. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/__init__.py +0 -0
  89. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/apps/__init__.py +0 -0
  90. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/apps/generate_concept_similarity_table.py +0 -0
  91. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/apps/generate_hierarchical_bert_training_data.py +0 -0
  92. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/apps/generate_information_content.py +0 -0
  93. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/apps/generate_required_labs.py +0 -0
  94. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/__init__.py +0 -0
  95. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/covid.py +0 -0
  96. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/covid_inpatient.py +0 -0
  97. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/death.py +0 -0
  98. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/heart_failure.py +0 -0
  99. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/query_builder.py +0 -0
  100. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/cohorts/ventilation.py +0 -0
  101. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/config/__init__.py +0 -0
  102. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/config/output_names.py +0 -0
  103. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/const/__init__.py +0 -0
  104. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/const/__pycache__/__init__.cpython-311.pyc +0 -0
  105. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/const/__pycache__/common.cpython-311.pyc +0 -0
  106. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/const/artificial_tokens.py +0 -0
  107. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/const/common.py +0 -0
  108. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/decorators/artificial_time_token_decorator.py +0 -0
  109. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/decorators/clinical_event_decorator.py +0 -0
  110. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/decorators/death_event_decorator.py +0 -0
  111. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/decorators/demographic_event_decorator.py +0 -0
  112. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/decorators/patient_event_decorator_base.py +0 -0
  113. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/__init__.py +0 -0
  114. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/afib_ischemic_stroke.py +0 -0
  115. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/cad_cabg_cohort.py +0 -0
  116. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/cad_hf_cohort.py +0 -0
  117. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/covid_death.py +0 -0
  118. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/covid_ventilation.py +0 -0
  119. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/discharge_home_death.py +0 -0
  120. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/hospitalization.py +0 -0
  121. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/prediction_cohorts/t2dm_hf_cohort.py +0 -0
  122. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/queries/__init__.py +0 -0
  123. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/queries/measurement_queries.py +0 -0
  124. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/tools/__init__.py +0 -0
  125. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/tools/connect_omop_visit.py +0 -0
  126. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/tools/download_omop_tables.py +0 -0
  127. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/tools/ehrshot_to_omop.py +0 -0
  128. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/tools/prepare_ehrshot_cohorts.py +0 -0
  129. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/tools/update_omop_visit.py +0 -0
  130. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/utils/__init__.py +0 -0
  131. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data/utils/logging_utils.py +0 -0
  132. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data.egg-info/dependency_links.txt +0 -0
  133. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data.egg-info/requires.txt +0 -0
  134. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/src/cehrbert_data.egg-info/top_level.txt +0 -0
  135. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/tests/__init__.py +0 -0
  136. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/tests/integration_tests/__init__.py +0 -0
  137. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/tests/integration_tests/test_generate_training_data.py +0 -0
  138. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/tests/integration_tests/test_hf_readmission_cohort_meds.py +0 -0
  139. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/tests/pyspark_test_base.py +0 -0
  140. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/tests/unit_tests/__init__.py +0 -0
  141. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/tests/unit_tests/test_ehrshot_to_omop.py +0 -0
  142. {cehrbert_data-0.0.6 → cehrbert_data-0.0.7}/tests/unit_tests/test_spark_utils.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: cehrbert_data
3
- Version: 0.0.6
3
+ Version: 0.0.7
4
4
  Summary: The Spark ETL tools for generating the CEHR-BERT and CEHR-GPT pre-training and finetuning data
5
5
  Author-email: Chao Pang <chaopang229@gmail.com>, Xinzhuo Jiang <xj2193@cumc.columbia.edu>, Krishna Kalluri <kk3326@cumc.columbia.edu>, Nishanth Parameshwar Pavinkurve <np2689@cumc.columbia.edu>, Karthik Natarajan <kn2174@cumc.columbia.edu>
6
6
  License: MIT License
@@ -25,6 +25,7 @@ Requires-Dist: pytest-subtests; extra == "dev"
25
25
  Requires-Dist: rootutils; extra == "dev"
26
26
  Requires-Dist: hypothesis; extra == "dev"
27
27
  Requires-Dist: black; extra == "dev"
28
+ Dynamic: license-file
28
29
 
29
30
  # cehrbert_data
30
31
 
@@ -18,6 +18,7 @@ Command-line Arguments:
18
18
  """
19
19
 
20
20
  import os
21
+ from typing import List
21
22
 
22
23
  from pyspark.sql import SparkSession
23
24
  from pyspark.sql import functions as F
@@ -29,7 +30,13 @@ from cehrbert_data.utils.spark_utils import join_domain_tables, preprocess_domai
29
30
  DOMAIN_TABLE_LIST = ["condition_occurrence", "procedure_occurrence", "drug_exposure"]
30
31
 
31
32
 
32
- def main(input_folder, output_folder, min_num_of_patients, with_drug_rollup: bool = True):
33
+ def main(
34
+ input_folder: str,
35
+ output_folder: str,
36
+ min_num_of_patients: int,
37
+ with_drug_rollup: bool = True,
38
+ ehr_table_list: List[str] = None
39
+ ):
33
40
  """
34
41
  Main function to generate a qualified concept list based on patient event data from multiple.
35
42
 
@@ -39,8 +46,9 @@ def main(input_folder, output_folder, min_num_of_patients, with_drug_rollup: boo
39
46
  input_folder (str): The directory where the input data is stored.
40
47
  output_folder (str): The directory where the output (qualified concept list) will be saved.
41
48
  min_num_of_patients (int): Minimum number of patients that a concept must be linked to for
42
- nclusion.
49
+ inclusion.
43
50
  with_drug_rollup (bool): If True, applies drug rollup logic to the drug_exposure domain.
51
+ ehr_table_list (List[str]): List of patient event tables to include in the concept list.
44
52
 
45
53
  The function processes patient event data across various domain tables, excludes low-frequency
46
54
  concepts, and saves the filtered concepts to a specified output folder.
@@ -50,7 +58,8 @@ def main(input_folder, output_folder, min_num_of_patients, with_drug_rollup: boo
50
58
  domain_tables = []
51
59
  # Exclude measurement from domain_table_list if exists because we need to process measurement
52
60
  # in a different way
53
- for domain_table_name in DOMAIN_TABLE_LIST:
61
+ domain_table_list = ehr_table_list if ehr_table_list else DOMAIN_TABLE_LIST
62
+ for domain_table_name in domain_table_list:
54
63
  if domain_table_name != MEASUREMENT:
55
64
  domain_tables.append(
56
65
  preprocess_domain_table(
@@ -105,6 +114,14 @@ if __name__ == "__main__":
105
114
  required=False,
106
115
  )
107
116
  parser.add_argument("--with_drug_rollup", dest="with_drug_rollup", action="store_true")
117
+ parser.add_argument(
118
+ "--ehr_table_list",
119
+ dest="ehr_table_list",
120
+ nargs="+",
121
+ action="store",
122
+ help="The list of domain tables you want to include for feature extraction",
123
+ required=False,
124
+ )
108
125
 
109
126
  ARGS = parser.parse_args()
110
127
 
@@ -113,4 +130,5 @@ if __name__ == "__main__":
113
130
  ARGS.output_folder,
114
131
  ARGS.min_num_of_patients,
115
132
  ARGS.with_drug_rollup,
133
+ ARGS.ehr_table_list,
116
134
  )
@@ -22,6 +22,7 @@ from cehrbert_data.utils.spark_utils import (
22
22
  preprocess_domain_table,
23
23
  get_measurement_table,
24
24
  validate_table_names,
25
+ construct_artificial_visits
25
26
  )
26
27
  from cehrbert_data.utils.logging_utils import add_console_logging
27
28
 
@@ -50,6 +51,7 @@ def main(
50
51
  continue_from_events: bool = False,
51
52
  refresh_measurement: bool = False,
52
53
  aggregate_by_hour: bool = True,
54
+ should_construct_artificial_visits: bool = False,
53
55
  ):
54
56
  spark = SparkSession.builder.appName("Generate CEHR-BERT Training Data").getOrCreate()
55
57
 
@@ -74,6 +76,7 @@ def main(
74
76
  f"with_drug_rollup: {with_drug_rollup}\n"
75
77
  f"refresh_measurement: {refresh_measurement}\n"
76
78
  f"aggregate_by_hour: {aggregate_by_hour}\n"
79
+ f"should_construct_artificial_visits: {should_construct_artificial_visits}\n"
77
80
  )
78
81
 
79
82
  domain_tables = []
@@ -94,6 +97,7 @@ def main(
94
97
  "visit_start_date",
95
98
  "visit_start_datetime",
96
99
  "visit_end_date",
100
+ "visit_end_datetime",
97
101
  "visit_concept_id",
98
102
  "person_id",
99
103
  "discharged_to_concept_id",
@@ -159,6 +163,15 @@ def main(
159
163
 
160
164
  patient_events = spark.read.parquet(os.path.join(output_folder, "all_patient_events"))
161
165
 
166
+ if should_construct_artificial_visits:
167
+ # Construct artificial visits or re-link the visits for the problem list events
168
+ patient_events, visit_occurrence_person = construct_artificial_visits(
169
+ patient_events,
170
+ visit_occurrence_person,
171
+ spark=spark,
172
+ persistence_folder=output_folder,
173
+ )
174
+
162
175
  if is_new_patient_representation:
163
176
  sequence_data = create_sequence_data_with_att(
164
177
  patient_events,
@@ -274,7 +287,7 @@ if __name__ == "__main__":
274
287
  type=lambda s: datetime.datetime.strptime(s, "%Y-%m-%d"),
275
288
  action="store",
276
289
  required=False,
277
- default="2018-01-01",
290
+ default="1985-01-01",
278
291
  )
279
292
  parser.add_argument(
280
293
  "-iv",
@@ -349,6 +362,13 @@ if __name__ == "__main__":
349
362
  action="store",
350
363
  choices=[e.value for e in AttType],
351
364
  )
365
+ parser.add_argument(
366
+ "--should_construct_artificial_visits",
367
+ dest="should_construct_artificial_visits",
368
+ action="store_true",
369
+ help="Indicate whether we should construct artificial visits for "
370
+ "the problem list records that could occur years ahead",
371
+ )
352
372
 
353
373
  ARGS = parser.parse_args()
354
374
 
@@ -378,4 +398,5 @@ if __name__ == "__main__":
378
398
  continue_from_events=ARGS.continue_from_events,
379
399
  refresh_measurement=ARGS.refresh_measurement,
380
400
  aggregate_by_hour=ARGS.aggregate_by_hour,
401
+ should_construct_artificial_visits=ARGS.should_construct_artificial_visits,
381
402
  )
@@ -0,0 +1,63 @@
1
+ from cehrbert_data.cohorts.query_builder import AncestorTableSpec, QueryBuilder, QuerySpec
2
+ from cehrbert_data.const.common import CONDITION_OCCURRENCE, PERSON, VISIT_OCCURRENCE
3
+
4
+ COHORT_QUERY_TEMPLATE = """
5
+ SELECT
6
+ DISTINCT
7
+ c.person_id,
8
+ c.index_date,
9
+ c.visit_occurrence_id
10
+ FROM
11
+ (
12
+ SELECT
13
+ co.person_id,
14
+ vo.visit_occurrence_id,
15
+ CASE
16
+ WHEN co.condition_start_datetime < vo.visit_start_datetime AND datediff(vo.visit_start_date, co.condition_start_date) <= 1
17
+ THEN to_timestamp(concat(date_format(vo.visit_start_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
18
+ ELSE
19
+ to_timestamp(concat(date_format(co.condition_start_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
20
+ END AS index_date,
21
+ ROW_NUMBER() OVER(
22
+ PARTITION BY co.person_id
23
+ ORDER BY co.condition_start_datetime,
24
+ co.condition_start_date,
25
+ co.visit_occurrence_id
26
+ ) as r_number
27
+ FROM global_temp.condition_occurrence AS co
28
+ JOIN global_temp.visit_occurrence AS vo
29
+ ON co.visit_occurrence_id = vo.visit_occurrence_id
30
+ JOIN global_temp.{atrial_fibrillation_concepts} AS c
31
+ ON co.condition_concept_id = c.concept_id
32
+ ) AS c
33
+ WHERE c.r_number = 1
34
+ """
35
+
36
+ ATRIAL_FIBRILLATION_CONCEPT_ID = [313217]
37
+
38
+ DEPENDENCY_LIST = [PERSON, VISIT_OCCURRENCE, CONDITION_OCCURRENCE]
39
+
40
+ DEFAULT_COHORT_NAME = "atrial_fibrillation"
41
+ ATRIAL_FIBRILLATION_CONCEPTS = "atrial_fibrillation_concepts"
42
+
43
+
44
+ def query_builder():
45
+ query = QuerySpec(
46
+ table_name=DEFAULT_COHORT_NAME,
47
+ query_template=COHORT_QUERY_TEMPLATE,
48
+ parameters={"atrial_fibrillation_concepts": ATRIAL_FIBRILLATION_CONCEPTS},
49
+ )
50
+
51
+ ancestor_table_specs = [
52
+ AncestorTableSpec(
53
+ table_name=ATRIAL_FIBRILLATION_CONCEPTS,
54
+ ancestor_concept_ids=ATRIAL_FIBRILLATION_CONCEPT_ID,
55
+ is_standard=True,
56
+ )
57
+ ]
58
+ return QueryBuilder(
59
+ cohort_name=DEFAULT_COHORT_NAME,
60
+ dependency_list=DEPENDENCY_LIST,
61
+ query=query,
62
+ ancestor_table_specs=ancestor_table_specs,
63
+ )
@@ -10,10 +10,18 @@ FROM
10
10
  (
11
11
  SELECT DISTINCT
12
12
  vo.person_id,
13
- FIRST(DATE(vo.visit_start_date)) OVER (PARTITION BY po.person_id
14
- ORDER BY DATE(vo.visit_start_date), vo.visit_occurrence_id) AS index_date,
15
- FIRST(vo.visit_occurrence_id) OVER (PARTITION BY po.person_id
16
- ORDER BY DATE(vo.visit_start_date), vo.visit_occurrence_id) AS visit_occurrence_id
13
+ vo.visit_occurrence_id,
14
+ CASE
15
+ WHEN po.procedure_datetime < vo.visit_start_datetime AND datediff(vo.visit_start_datetime, po.procedure_datetime) <= 1
16
+ THEN to_timestamp(concat(date_format(vo.visit_start_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
17
+ ELSE to_timestamp(concat(date_format(po.procedure_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
18
+ END AS index_date,
19
+ ROW_NUMBER() OVER(
20
+ PARTITION BY po.person_id
21
+ ORDER BY po.procedure_datetime,
22
+ po.procedure_date,
23
+ po.visit_occurrence_id
24
+ ) as r_number
17
25
  FROM global_temp.procedure_occurrence AS po
18
26
  JOIN global_temp.visit_occurrence AS vo
19
27
  ON po.visit_occurrence_id = vo.visit_occurrence_id
@@ -23,7 +31,7 @@ FROM
23
31
  WHERE po.procedure_concept_id = ie.concept_id
24
32
  )
25
33
  ) c
26
- WHERE c.index_date >= '{date_lower_bound}'
34
+ WHERE c.index_date >= '{date_lower_bound}' AND c.r_number = 1
27
35
  """
28
36
 
29
37
  DEFAULT_COHORT_NAME = "cabg"
@@ -21,10 +21,18 @@ FROM
21
21
  (
22
22
  SELECT DISTINCT
23
23
  vo.person_id,
24
- FIRST(DATE(vo.visit_start_date)) OVER (PARTITION BY co.person_id
25
- ORDER BY DATE(vo.visit_start_date), vo.visit_occurrence_id) AS index_date,
26
- FIRST(vo.visit_occurrence_id) OVER (PARTITION BY co.person_id
27
- ORDER BY DATE(vo.visit_start_date), vo.visit_occurrence_id) AS visit_occurrence_id
24
+ vo.visit_occurrence_id,
25
+ CASE
26
+ WHEN co.condition_start_datetime < vo.visit_start_datetime AND datediff(vo.visit_start_datetime, co.condition_start_datetime) <= 1
27
+ THEN to_timestamp(concat(date_format(vo.visit_start_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
28
+ ELSE to_timestamp(concat(date_format(co.condition_start_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
29
+ END AS index_date,
30
+ ROW_NUMBER() OVER(
31
+ PARTITION BY co.person_id
32
+ ORDER BY co.condition_start_datetime,
33
+ co.condition_start_date,
34
+ co.visit_occurrence_id
35
+ ) as r_number
28
36
  FROM global_temp.condition_occurrence AS co
29
37
  JOIN global_temp.visit_occurrence AS vo
30
38
  ON co.visit_occurrence_id = vo.visit_occurrence_id
@@ -41,7 +49,9 @@ WHERE NOT EXISTS (
41
49
  FROM prior_graft_stent AS exclusion
42
50
  WHERE exclusion.person_id = c.person_id
43
51
  AND c.index_date > exclusion.procedure_date
44
- ) AND c.index_date >= '{date_lower_bound}'
52
+ )
53
+ AND c.r_number = 1
54
+ AND c.index_date >= '{date_lower_bound}'
45
55
  """
46
56
 
47
57
  DEFAULT_COHORT_NAME = "coronary_artery_disease"
@@ -0,0 +1,61 @@
1
+ from cehrbert_data.cohorts.query_builder import AncestorTableSpec, QueryBuilder, QuerySpec
2
+ from cehrbert_data.const.common import CONDITION_OCCURRENCE, PERSON, VISIT_OCCURRENCE
3
+
4
+ COHORT_QUERY_TEMPLATE = """
5
+ SELECT DISTINCT
6
+ c.person_id,
7
+ c.index_date,
8
+ c.visit_occurrence_id
9
+ FROM
10
+ (
11
+ SELECT
12
+ co.person_id,
13
+ vo.visit_occurrence_id,
14
+ CASE
15
+ WHEN co.condition_start_datetime < vo.visit_start_datetime AND datediff(vo.visit_start_datetime, co.condition_start_datetime) <= 1
16
+ THEN to_timestamp(concat(date_format(vo.visit_start_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
17
+ ELSE to_timestamp(concat(date_format(co.condition_start_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
18
+ END AS index_date,
19
+ ROW_NUMBER() OVER(
20
+ PARTITION BY co.person_id
21
+ ORDER BY co.condition_start_datetime,
22
+ co.condition_start_date,
23
+ co.visit_occurrence_id
24
+ ) as r_number
25
+ FROM global_temp.condition_occurrence AS co
26
+ JOIN global_temp.visit_occurrence AS vo
27
+ ON co.visit_occurrence_id = vo.visit_occurrence_id
28
+ JOIN global_temp.{ischemic_stroke_concepts} AS c
29
+ ON co.condition_concept_id = c.concept_id
30
+ ) AS c
31
+ WHERE c.r_number = 1
32
+ """
33
+
34
+ ISCHEMIC_STROKE_CONCEPT_ID = [443454]
35
+
36
+ DEPENDENCY_LIST = [PERSON, VISIT_OCCURRENCE, CONDITION_OCCURRENCE]
37
+
38
+ DEFAULT_COHORT_NAME = "ischemic_stroke"
39
+ ISCHEMIC_STROKE_CONCEPTS = "ischemic_stroke_concepts"
40
+
41
+
42
+ def query_builder():
43
+ query = QuerySpec(
44
+ table_name=DEFAULT_COHORT_NAME,
45
+ query_template=COHORT_QUERY_TEMPLATE,
46
+ parameters={"ischemic_stroke_concepts": ISCHEMIC_STROKE_CONCEPTS},
47
+ )
48
+
49
+ ancestor_table_specs = [
50
+ AncestorTableSpec(
51
+ table_name=ISCHEMIC_STROKE_CONCEPTS,
52
+ ancestor_concept_ids=ISCHEMIC_STROKE_CONCEPT_ID,
53
+ is_standard=True,
54
+ )
55
+ ]
56
+ return QueryBuilder(
57
+ cohort_name=DEFAULT_COHORT_NAME,
58
+ dependency_list=DEPENDENCY_LIST,
59
+ query=query,
60
+ ancestor_table_specs=ancestor_table_specs,
61
+ )
@@ -11,7 +11,7 @@ FROM
11
11
  SELECT
12
12
  v.person_id,
13
13
  v.visit_occurrence_id,
14
- v.visit_end_date AS index_date,
14
+ coalesce(v.visit_end_datetime, v.visit_end_date) AS index_date,
15
15
  v.discharged_to_concept_id,
16
16
  ROW_NUMBER() OVER(PARTITION BY v.person_id ORDER BY DATE(v.visit_end_date) DESC) AS rn
17
17
  FROM global_temp.visit_occurrence AS v
@@ -5,7 +5,6 @@ import shutil
5
5
  from abc import ABC
6
6
  from typing import List
7
7
 
8
- from numpy.random import permutation
9
8
  from pandas import to_datetime
10
9
  from pyspark.sql import DataFrame, SparkSession
11
10
  from pyspark.sql import functions as F
@@ -22,6 +21,7 @@ from cehrbert_data.utils.spark_utils import (
22
21
  extract_ehr_records,
23
22
  get_descendant_concept_ids,
24
23
  preprocess_domain_table,
24
+ construct_artificial_visits
25
25
  )
26
26
  from cehrbert_data.utils.logging_utils import add_console_logging
27
27
 
@@ -232,8 +232,8 @@ class BaseCohortBuilder(ABC):
232
232
  FROM global_temp.cohort AS c
233
233
  JOIN global_temp.observation_period AS p
234
234
  ON c.person_id = p.person_id
235
- AND DATE_ADD(c.index_date, -{prior_observation_period}) >= p.observation_period_start_date
236
- AND DATE_ADD(c.index_date, {post_observation_period}) <= p.observation_period_end_date
235
+ AND c.index_date - INTERVAL {prior_observation_period} DAY >= p.observation_period_start_date
236
+ AND c.index_date + INTERVAL {post_observation_period} DAY <= p.observation_period_end_date
237
237
  """.format(
238
238
  prior_observation_period=self._prior_observation_period,
239
239
  post_observation_period=self._post_observation_period,
@@ -317,6 +317,7 @@ class NestedCohortBuilder:
317
317
  exclude_features: bool = True,
318
318
  meds_format: bool = False,
319
319
  cache_events: bool = False,
320
+ should_construct_artificial_visits: bool = False,
320
321
  ):
321
322
  self._cohort_name = cohort_name
322
323
  self._input_folder = input_folder
@@ -363,6 +364,7 @@ class NestedCohortBuilder:
363
364
  self._exclude_features = exclude_features
364
365
  self._meds_format = meds_format
365
366
  self._cache_events = cache_events
367
+ self._should_construct_artificial_visits = should_construct_artificial_visits
366
368
 
367
369
  self.get_logger().info(
368
370
  f"cohort_name: {cohort_name}\n"
@@ -403,6 +405,7 @@ class NestedCohortBuilder:
403
405
  f"extract_features: {exclude_features}\n"
404
406
  f"meds_format: {meds_format}\n"
405
407
  f"cache_events: {cache_events}\n"
408
+ f"should_construct_artificial_visits: {should_construct_artificial_visits}\n"
406
409
  )
407
410
 
408
411
  self.spark = SparkSession.builder.appName(f"Generate {self._cohort_name}").getOrCreate()
@@ -434,7 +437,7 @@ class NestedCohortBuilder:
434
437
  FROM global_temp.target_cohort AS t
435
438
  LEFT JOIN global_temp.{entry_cohort} AS o
436
439
  ON t.person_id = o.person_id
437
- AND DATE_ADD(t.index_date, {prediction_start_days}) > o.index_date
440
+ AND t.index_date + INTERVAL {prediction_start_days} DAY > o.index_date
438
441
  WHERE o.person_id IS NULL
439
442
  """.format(
440
443
  entry_cohort=ENTRY_COHORT,
@@ -468,7 +471,7 @@ class NestedCohortBuilder:
468
471
  LEFT JOIN global_temp.outcome_cohort AS exclusion
469
472
  ON t.person_id = exclusion.person_id
470
473
  AND exclusion.index_date BETWEEN t.index_date
471
- AND DATE_ADD(t.index_date, {prediction_start_days})
474
+ AND t.index_date + INTERVAL {prediction_start_days} DAY
472
475
  WHERE exclusion.person_id IS NULL
473
476
  """.format(
474
477
  prediction_start_days=max(prediction_start_days - 1, 0)
@@ -485,7 +488,7 @@ class NestedCohortBuilder:
485
488
  FROM global_temp.target_cohort AS t
486
489
  LEFT JOIN global_temp.outcome_cohort AS o
487
490
  ON t.person_id = o.person_id
488
- AND o.index_date >= DATE_ADD(t.index_date, {prediction_start_days})
491
+ AND o.index_date >= t.index_date + INTERVAL {prediction_start_days} DAY
489
492
  """
490
493
  else:
491
494
  query_template = """
@@ -496,11 +499,11 @@ class NestedCohortBuilder:
496
499
  FROM global_temp.target_cohort AS t
497
500
  LEFT JOIN global_temp.observation_period AS op
498
501
  ON t.person_id = op.person_id
499
- AND DATE_ADD(t.index_date, {prediction_window}) <= op.observation_period_end_date
502
+ AND t.index_date + INTERVAL {prediction_window} DAY <= op.observation_period_end_date
500
503
  LEFT JOIN global_temp.outcome_cohort AS o
501
504
  ON t.person_id = o.person_id
502
- AND o.index_date BETWEEN DATE_ADD(t.index_date, {prediction_start_days})
503
- AND DATE_ADD(t.index_date, {prediction_window})
505
+ AND o.index_date BETWEEN t.index_date + INTERVAL {prediction_start_days} DAY
506
+ AND t.index_date + INTERVAL {prediction_window} DAY
504
507
  WHERE op.person_id IS NOT NULL OR o.person_id IS NOT NULL
505
508
  """
506
509
 
@@ -557,20 +560,24 @@ class NestedCohortBuilder:
557
560
  observation_period = self._dependency_dict[OBSERVATION_PERIOD]
558
561
  # Add time_to_event
559
562
  cohort = cohort.join(
560
- observation_period.select("person_id", "observation_period_end_date"), "person_id"
563
+ observation_period.select("person_id", "observation_period_end_date"),
564
+ cohort[person_id_column] == observation_period["person_id"]
565
+ ).select(
566
+ [cohort[c] for c in cohort.columns] +
567
+ [observation_period["observation_period_end_date"]]
561
568
  ).withColumn(
562
569
  "study_end_date",
563
570
  F.coalesce(F.col("outcome_date"), F.col("observation_period_end_date"))
564
- ).drop("observation_period_end_date")
571
+ ).drop(
572
+ "observation_period_end_date"
573
+ )
565
574
  else:
566
575
  # Add time_to_event
567
576
  cohort = cohort.withColumn(
568
577
  "study_end_date",
569
578
  F.coalesce(
570
579
  F.col("outcome_date"),
571
- F.date_add(
572
- cohort[index_date_column], self._prediction_window
573
- )
580
+ F.expr(f"{index_date_column} + INTERVAL {self._prediction_window} DAYS")
574
581
  )
575
582
  )
576
583
  cohort = cohort.withColumn("time_to_event", F.datediff("study_end_date", index_date_column))
@@ -578,11 +585,11 @@ class NestedCohortBuilder:
578
585
  # if patient_splits is provided, we will
579
586
  if self._patient_splits_folder:
580
587
  patient_splits = self.spark.read.parquet(self._patient_splits_folder)
581
- cohort.join(
582
- patient_splits,
583
- cohort[person_id_column] == patient_splits.person_id
588
+ cohort.alias("cohort").join(
589
+ patient_splits.alias("split"),
590
+ F.col(f"cohort.{person_id_column}") == F.col("split.person_id")
584
591
  ).select(
585
- [cohort[c] for c in cohort.columns] + [patient_splits.split]
592
+ [F.col(f"cohort.{c}").alias(c) for c in cohort.columns] + [F.col("split.split").alias("split")]
586
593
  ).orderBy(person_id_column, index_date_column).write.mode(
587
594
  "overwrite"
588
595
  ).parquet(
@@ -596,7 +603,9 @@ class NestedCohortBuilder:
596
603
  cohort.where('split="test"').write.mode("overwrite").parquet(os.path.join(self._output_data_folder, "test"))
597
604
  shutil.rmtree(os.path.join(self._output_data_folder, "temp"))
598
605
  else:
599
- cohort.orderBy(person_id_column, index_date_column).write.mode("overwrite").parquet(self._output_data_folder)
606
+ cohort.orderBy(person_id_column, index_date_column).write.mode("overwrite").parquet(
607
+ os.path.join(self._output_data_folder, "data")
608
+ )
600
609
 
601
610
  def extract_ehr_records_for_cohort(self, cohort: DataFrame):
602
611
  """
@@ -618,6 +627,25 @@ class NestedCohortBuilder:
618
627
  aggregate_by_hour=self._aggregate_by_hour,
619
628
  )
620
629
 
630
+ if self._cache_events:
631
+ all_patient_events_dir = os.path.join(self._output_data_folder, "all_patient_events")
632
+ ehr_records.write.mode("overwrite").parquet(
633
+ all_patient_events_dir
634
+ )
635
+ ehr_records = self.spark.read.parquet(
636
+ all_patient_events_dir
637
+ )
638
+
639
+ if self._should_construct_artificial_visits:
640
+ ehr_records, visit_occurrence_with_artificial_visits = construct_artificial_visits(
641
+ ehr_records,
642
+ self._dependency_dict[VISIT_OCCURRENCE],
643
+ spark=self.spark if self._cache_events else None,
644
+ persistence_folder=self._output_data_folder if self._cache_events else None,
645
+ )
646
+ # Refresh the dependency
647
+ self._dependency_dict[VISIT_OCCURRENCE] = visit_occurrence_with_artificial_visits
648
+
621
649
  # Duplicate the records for cohorts that allow multiple entries
622
650
  ehr_records = ehr_records.alias("ehr").join(
623
651
  cohort.alias("cohort"), F.col("ehr.person_id") == F.col("cohort.person_id")
@@ -628,30 +656,29 @@ class NestedCohortBuilder:
628
656
  # Only allow the data records that occurred between the index date and the prediction window
629
657
  if self._is_population_estimation:
630
658
  if self._is_prediction_window_unbounded:
631
- record_window_filter = F.col("ehr.date") <= F.current_date()
659
+ record_window_filter = F.col("ehr.datetime") <= F.current_timestamp()
632
660
  else:
633
- record_window_filter = F.col("ehr.date") <= F.date_add(
634
- F.col("cohort.index_date"), self._prediction_window
661
+ record_window_filter = (
662
+ F.col("ehr.datetime") <=
663
+ F.expr(f"cohort.index_date - INTERVAL {self._hold_off_window} DAYS + INTERVAL 0.1 SECOND")
635
664
  )
636
665
  else:
637
666
  # For patient level prediction, we remove all records post index date
638
667
  if self._is_observation_post_index:
639
- record_window_filter = F.col("ehr.date").between(
668
+ record_window_filter = F.col("ehr.datetime").between(
640
669
  F.col("cohort.index_date"),
641
- F.date_add(F.col("cohort.index_date"), self._observation_window),
670
+ F.expr(f"cohort.index_date + INTERVAL {self._observation_window} DAYS + INTERVAL 0.1 SECOND")
642
671
  )
643
672
  else:
644
673
  if self._is_observation_window_unbounded:
645
- record_window_filter = F.col("ehr.date") <= F.date_sub(
646
- F.col("cohort.index_date"), self._hold_off_window
674
+ record_window_filter = (
675
+ F.col("ehr.datetime")
676
+ <= F.expr(f"cohort.index_date - INTERVAL {self._hold_off_window} DAYS + INTERVAL 0.1 SECOND")
647
677
  )
648
678
  else:
649
- record_window_filter = F.col("ehr.date").between(
650
- F.date_sub(
651
- F.col("cohort.index_date"),
652
- self._observation_window + self._hold_off_window,
653
- ),
654
- F.date_sub(F.col("cohort.index_date"), self._hold_off_window),
679
+ record_window_filter = F.col("ehr.datetime").between(
680
+ F.expr(f"cohort.index_date - INTERVAL {self._observation_window + self._hold_off_window} DAYS"),
681
+ F.expr(f"cohort.index_date - INTERVAL {self._hold_off_window} DAYS + INTERVAL 0.1 SECOND"),
655
682
  )
656
683
 
657
684
  # Somehow the dataframe join does not work without using the alias
@@ -709,6 +736,7 @@ class NestedCohortBuilder:
709
736
  include_inpatient_hour_token=self._include_inpatient_hour_token,
710
737
  spark=self.spark if self._cache_events else None,
711
738
  persistence_folder=self._output_data_folder if self._cache_events else None,
739
+ cohort_index=cohort.select("person_id", "cohort_member_id", "index_date")
712
740
  )
713
741
 
714
742
  return create_sequence_data(
@@ -832,4 +860,6 @@ def create_prediction_cohort(
832
860
  single_contribution=spark_args.single_contribution,
833
861
  exclude_features=spark_args.exclude_features,
834
862
  meds_format=spark_args.meds_format,
863
+ cache_events=spark_args.cache_events,
864
+ should_construct_artificial_visits=spark_args.should_construct_artificial_visits,
835
865
  ).build()
@@ -20,8 +20,7 @@ person_ids_to_exclude_observation AS
20
20
  JOIN global_temp.{observation_exclusion_concepts} AS oec
21
21
  ON o.observation_concept_id = oec.concept_id
22
22
  )
23
- SELECT
24
- distinct
23
+ SELECT DISTINCT
25
24
  c.person_id,
26
25
  c.index_date,
27
26
  c.visit_occurrence_id
@@ -29,10 +28,18 @@ FROM
29
28
  (
30
29
  SELECT DISTINCT
31
30
  vo.person_id,
32
- FIRST(DATE(vo.visit_start_date)) OVER (PARTITION BY co.person_id
33
- ORDER BY DATE(vo.visit_start_date), vo.visit_occurrence_id) AS index_date,
34
- FIRST(vo.visit_occurrence_id) OVER (PARTITION BY co.person_id
35
- ORDER BY DATE(vo.visit_start_date), vo.visit_occurrence_id) AS visit_occurrence_id
31
+ vo.visit_occurrence_id,
32
+ CASE
33
+ WHEN co.condition_start_datetime < vo.visit_start_datetime AND datediff(vo.visit_start_datetime, co.condition_start_datetime) <= 1
34
+ THEN to_timestamp(concat(date_format(vo.visit_start_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
35
+ ELSE to_timestamp(concat(date_format(co.condition_start_date, 'yyyy-MM-dd'), ' 23:59:00'), 'yyyy-MM-dd HH:mm:ss')
36
+ END AS index_date,
37
+ ROW_NUMBER() OVER(
38
+ PARTITION BY co.person_id
39
+ ORDER BY co.condition_start_datetime,
40
+ co.condition_start_date,
41
+ co.visit_occurrence_id
42
+ ) as r_number
36
43
  FROM global_temp.condition_occurrence AS co
37
44
  JOIN global_temp.{diabetes_inclusion_concepts} AS ie
38
45
  ON co.condition_concept_id = ie.concept_id
@@ -43,7 +50,9 @@ JOIN person_ids_to_include_drug AS d
43
50
  ON c.person_id = d.person_id
44
51
  LEFT JOIN person_ids_to_exclude_observation AS eo
45
52
  ON c.person_id = eo.person_id AND c.index_date > eo.observation_date
46
- WHERE eo.person_id IS NULL AND c.index_date >= '{date_lower_bound}'
53
+ WHERE eo.person_id IS NULL
54
+ AND c.r_number = 1
55
+ AND c.index_date >= '{date_lower_bound}'
47
56
  """
48
57
 
49
58
  DIABETES_INCLUSION = [443238, 201820, 442793, 4016045]
@@ -2,4 +2,5 @@ from .artificial_time_token_decorator import AttEventDecorator
2
2
  from .death_event_decorator import DeathEventDecorator
3
3
  from .clinical_event_decorator import ClinicalEventDecorator
4
4
  from .demographic_event_decorator import DemographicEventDecorator
5
+ from .prediction_token_decorator import PredictionEventDecorator
5
6
  from .patient_event_decorator_base import time_token_func, get_att_function, AttType