cehrbert-data 0.0.7__tar.gz → 0.0.8__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 (141) hide show
  1. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/PKG-INFO +1 -1
  2. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/apps/generate_included_concept_list.py +23 -17
  3. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/apps/generate_training_data.py +70 -74
  4. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/spark_app_base.py +35 -17
  5. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/const/common.py +2 -0
  6. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/decorators/clinical_event_decorator.py +11 -4
  7. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/tools/extract_features.py +30 -18
  8. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/utils/spark_parse_args.py +6 -0
  9. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/utils/spark_utils.py +285 -785
  10. cehrbert_data-0.0.8/src/cehrbert_data/utils/vocab_utils.py +361 -0
  11. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data.egg-info/PKG-INFO +1 -1
  12. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data.egg-info/SOURCES.txt +1 -4
  13. cehrbert_data-0.0.7/src/cehrbert_data/apps/generate_concept_similarity_table.py +0 -423
  14. cehrbert_data-0.0.7/src/cehrbert_data/apps/generate_hierarchical_bert_training_data.py +0 -238
  15. cehrbert_data-0.0.7/src/cehrbert_data/apps/generate_information_content.py +0 -131
  16. cehrbert_data-0.0.7/src/cehrbert_data/apps/generate_required_labs.py +0 -140
  17. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/.github/workflows/python-build.yml +0 -0
  18. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/.github/workflows/tests.yml +0 -0
  19. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/.gitignore +0 -0
  20. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/.pre-commit-config.yaml +0 -0
  21. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/LICENSE +0 -0
  22. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/README.md +0 -0
  23. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/pyproject.toml +0 -0
  24. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept/._SUCCESS.crc +0 -0
  25. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept/.part-00000-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet.crc +0 -0
  26. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept/.part-00003-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet.crc +0 -0
  27. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept/.part-00010-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet.crc +0 -0
  28. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept/_SUCCESS +0 -0
  29. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept/part-00000-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet +0 -0
  30. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept/part-00003-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet +0 -0
  31. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept/part-00010-4b12270c-f6c8-4b59-8e0f-fd588bd79386-c000.snappy.parquet +0 -0
  32. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/._SUCCESS.crc +0 -0
  33. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/.part-00000-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  34. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/.part-00002-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  35. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/.part-00006-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  36. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/.part-00011-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  37. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/.part-00013-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet.crc +0 -0
  38. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/_SUCCESS +0 -0
  39. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/part-00000-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  40. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/part-00002-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  41. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/part-00006-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  42. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/part-00011-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  43. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_ancestor/part-00013-eafbd8be-3337-46da-89d3-20f79c2565d4-c000.snappy.parquet +0 -0
  44. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/._SUCCESS.crc +0 -0
  45. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/.part-00000-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet.crc +0 -0
  46. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/.part-00002-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet.crc +0 -0
  47. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/.part-00007-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet.crc +0 -0
  48. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/.part-00012-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet.crc +0 -0
  49. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/_SUCCESS +0 -0
  50. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/part-00000-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet +0 -0
  51. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/part-00002-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet +0 -0
  52. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/part-00007-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet +0 -0
  53. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/concept_relationship/part-00012-5752b472-8ba7-4189-ab69-8c92e46443aa-c000.snappy.parquet +0 -0
  54. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/condition_occurrence/._SUCCESS.crc +0 -0
  55. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/condition_occurrence/.part-00000-4eff03a1-cdcf-4c89-b0cd-9ce590b9b1eb-c000.snappy.parquet.crc +0 -0
  56. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/condition_occurrence/_SUCCESS +0 -0
  57. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/condition_occurrence/part-00000-4eff03a1-cdcf-4c89-b0cd-9ce590b9b1eb-c000.snappy.parquet +0 -0
  58. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/drug_exposure/._SUCCESS.crc +0 -0
  59. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/drug_exposure/.part-00000-10bbf1a4-a7da-416e-9703-58609c7edfad-c000.snappy.parquet.crc +0 -0
  60. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/drug_exposure/_SUCCESS +0 -0
  61. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/drug_exposure/part-00000-10bbf1a4-a7da-416e-9703-58609c7edfad-c000.snappy.parquet +0 -0
  62. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/observation_period/._SUCCESS.crc +0 -0
  63. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/observation_period/.part-00000-694316e5-cc95-49f1-9fad-5a7f377e2602-c000.snappy.parquet.crc +0 -0
  64. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/observation_period/_SUCCESS +0 -0
  65. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/observation_period/part-00000-694316e5-cc95-49f1-9fad-5a7f377e2602-c000.snappy.parquet +0 -0
  66. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/person/._SUCCESS.crc +0 -0
  67. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/person/.part-00000-7d789011-f361-48da-af6f-cfe102978b3a-c000.snappy.parquet.crc +0 -0
  68. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/person/_SUCCESS +0 -0
  69. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/person/part-00000-7d789011-f361-48da-af6f-cfe102978b3a-c000.snappy.parquet +0 -0
  70. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/procedure_occurrence/._SUCCESS.crc +0 -0
  71. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/procedure_occurrence/.part-00000-e73003c1-aed5-41c0-b2d4-eaccaccf044a-c000.snappy.parquet.crc +0 -0
  72. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/procedure_occurrence/_SUCCESS +0 -0
  73. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/procedure_occurrence/part-00000-e73003c1-aed5-41c0-b2d4-eaccaccf044a-c000.snappy.parquet +0 -0
  74. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/visit_occurrence/._SUCCESS.crc +0 -0
  75. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/visit_occurrence/.part-00000-e874b5f1-bf9e-4cb9-93bf-c309a47b0476-c000.snappy.parquet.crc +0 -0
  76. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/visit_occurrence/_SUCCESS +0 -0
  77. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/sample_data/omop_sample/visit_occurrence/part-00000-e874b5f1-bf9e-4cb9-93bf-c309a47b0476-c000.snappy.parquet +0 -0
  78. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/setup.cfg +0 -0
  79. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/__init__.py +0 -0
  80. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/__init__.py +0 -0
  81. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/apps/__init__.py +0 -0
  82. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/__init__.py +0 -0
  83. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/atrial_fibrillation.py +0 -0
  84. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/cabg.py +0 -0
  85. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/coronary_artery_disease.py +0 -0
  86. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/covid.py +0 -0
  87. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/covid_inpatient.py +0 -0
  88. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/death.py +0 -0
  89. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/heart_failure.py +0 -0
  90. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/ischemic_stroke.py +0 -0
  91. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/last_visit_discharged_home.py +0 -0
  92. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/query_builder.py +0 -0
  93. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/type_two_diabietes.py +0 -0
  94. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/cohorts/ventilation.py +0 -0
  95. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/config/__init__.py +0 -0
  96. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/config/output_names.py +0 -0
  97. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/const/__init__.py +0 -0
  98. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/const/__pycache__/__init__.cpython-311.pyc +0 -0
  99. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/const/__pycache__/common.cpython-311.pyc +0 -0
  100. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/const/artificial_tokens.py +0 -0
  101. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/decorators/__init__.py +0 -0
  102. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/decorators/artificial_time_token_decorator.py +0 -0
  103. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/decorators/death_event_decorator.py +0 -0
  104. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/decorators/demographic_event_decorator.py +0 -0
  105. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/decorators/patient_event_decorator_base.py +0 -0
  106. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/decorators/prediction_token_decorator.py +0 -0
  107. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/decorators/token_priority.py +0 -0
  108. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/__init__.py +0 -0
  109. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/afib_ischemic_stroke.py +0 -0
  110. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/cad_cabg_cohort.py +0 -0
  111. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/cad_hf_cohort.py +0 -0
  112. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/copd_readmission.py +0 -0
  113. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/covid_death.py +0 -0
  114. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/covid_ventilation.py +0 -0
  115. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/discharge_home_death.py +0 -0
  116. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/hf_readmission.py +0 -0
  117. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/hospitalization.py +0 -0
  118. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/hospitalization_mortality.py +0 -0
  119. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/prediction_cohorts/t2dm_hf_cohort.py +0 -0
  120. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/queries/__init__.py +0 -0
  121. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/queries/measurement_queries.py +0 -0
  122. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/tools/__init__.py +0 -0
  123. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/tools/connect_omop_visit.py +0 -0
  124. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/tools/download_omop_tables.py +0 -0
  125. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/tools/ehrshot_to_omop.py +0 -0
  126. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/tools/prepare_ehrshot_cohorts.py +0 -0
  127. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/tools/update_omop_visit.py +0 -0
  128. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/utils/__init__.py +0 -0
  129. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data/utils/logging_utils.py +0 -0
  130. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data.egg-info/dependency_links.txt +0 -0
  131. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data.egg-info/requires.txt +0 -0
  132. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/src/cehrbert_data.egg-info/top_level.txt +0 -0
  133. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/tests/__init__.py +0 -0
  134. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/tests/integration_tests/__init__.py +0 -0
  135. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/tests/integration_tests/test_generate_training_data.py +0 -0
  136. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/tests/integration_tests/test_hf_readmission.py +0 -0
  137. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/tests/integration_tests/test_hf_readmission_cohort_meds.py +0 -0
  138. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/tests/pyspark_test_base.py +0 -0
  139. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/tests/unit_tests/__init__.py +0 -0
  140. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/tests/unit_tests/test_ehrshot_to_omop.py +0 -0
  141. {cehrbert_data-0.0.7 → cehrbert_data-0.0.8}/tests/unit_tests/test_spark_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cehrbert_data
3
- Version: 0.0.7
3
+ Version: 0.0.8
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
@@ -24,8 +24,8 @@ from pyspark.sql import SparkSession
24
24
  from pyspark.sql import functions as F
25
25
 
26
26
  from cehrbert_data.config.output_names import QUALIFIED_CONCEPT_LIST_PATH
27
- from cehrbert_data.const.common import MEASUREMENT
28
- from cehrbert_data.utils.spark_utils import join_domain_tables, preprocess_domain_table
27
+ from cehrbert_data.const.common import CONCEPT
28
+ from cehrbert_data.utils.spark_utils import extract_events_by_domain, preprocess_domain_table
29
29
 
30
30
  DOMAIN_TABLE_LIST = ["condition_occurrence", "procedure_occurrence", "drug_exposure"]
31
31
 
@@ -55,32 +55,38 @@ def main(
55
55
  """
56
56
  spark = SparkSession.builder.appName("Generate concept list").getOrCreate()
57
57
 
58
- domain_tables = []
59
58
  # Exclude measurement from domain_table_list if exists because we need to process measurement
60
59
  # in a different way
61
60
  domain_table_list = ehr_table_list if ehr_table_list else DOMAIN_TABLE_LIST
61
+ concept = preprocess_domain_table(spark, input_folder, CONCEPT)
62
+ patient_ehr_events = None
62
63
  for domain_table_name in domain_table_list:
63
- if domain_table_name != MEASUREMENT:
64
- domain_tables.append(
65
- preprocess_domain_table(
66
- spark,
67
- input_folder,
68
- domain_table_name,
69
- with_drug_rollup=with_drug_rollup,
70
- )
71
- )
72
-
73
- # Union all domain table records
74
- patient_events = join_domain_tables(domain_tables)
64
+ domain_table = preprocess_domain_table(
65
+ spark=spark,
66
+ input_folder=input_folder,
67
+ domain_table_name=domain_table_name,
68
+ with_drug_rollup=with_drug_rollup
69
+ )
70
+ ehr_events = extract_events_by_domain(
71
+ domain_table,
72
+ spark=spark,
73
+ concept=concept,
74
+ aggregate_by_hour=False,
75
+ refresh=False,
76
+ persistence_folder=input_folder
77
+ )
78
+ if patient_ehr_events is None:
79
+ patient_ehr_events = ehr_events
80
+ else:
81
+ patient_ehr_events = patient_ehr_events.unionByName(ehr_events)
75
82
 
76
83
  # Filter out concepts that are linked to less than 100 patients
77
84
  qualified_concepts = (
78
- patient_events.where("visit_occurrence_id IS NOT NULL")
85
+ patient_ehr_events.where("visit_occurrence_id IS NOT NULL")
79
86
  .groupBy("standard_concept_id")
80
87
  .agg(F.countDistinct("person_id").alias("freq"))
81
88
  .where(F.col("freq") >= min_num_of_patients)
82
89
  )
83
-
84
90
  qualified_concepts.write.mode("overwrite").parquet(os.path.join(output_folder, QUALIFIED_CONCEPT_LIST_PATH))
85
91
 
86
92
 
@@ -12,15 +12,14 @@ from cehrbert_data.const.common import (
12
12
  PERSON,
13
13
  VISIT_OCCURRENCE,
14
14
  DEATH,
15
- MEASUREMENT
15
+ CONCEPT
16
16
  )
17
17
  from cehrbert_data.decorators import AttType
18
18
  from cehrbert_data.utils.spark_utils import (
19
19
  create_sequence_data,
20
20
  create_sequence_data_with_att,
21
- join_domain_tables,
22
21
  preprocess_domain_table,
23
- get_measurement_table,
22
+ extract_events_by_domain,
24
23
  validate_table_names,
25
24
  construct_artificial_visits
26
25
  )
@@ -43,7 +42,6 @@ def main(
43
42
  include_death: bool,
44
43
  att_type: AttType,
45
44
  inpatient_att_type: AttType,
46
- include_sequence_information_content: bool = False,
47
45
  exclude_demographic: bool = False,
48
46
  use_age_group: bool = False,
49
47
  with_drug_rollup: bool = True,
@@ -52,6 +50,7 @@ def main(
52
50
  refresh_measurement: bool = False,
53
51
  aggregate_by_hour: bool = True,
54
52
  should_construct_artificial_visits: bool = False,
53
+ duplicate_records: bool = False
55
54
  ):
56
55
  spark = SparkSession.builder.appName("Generate CEHR-BERT Training Data").getOrCreate()
57
56
 
@@ -77,19 +76,30 @@ def main(
77
76
  f"refresh_measurement: {refresh_measurement}\n"
78
77
  f"aggregate_by_hour: {aggregate_by_hour}\n"
79
78
  f"should_construct_artificial_visits: {should_construct_artificial_visits}\n"
79
+ f"duplicate_records: {duplicate_records}\n"
80
80
  )
81
81
 
82
- domain_tables = []
82
+ concept = preprocess_domain_table(spark, input_folder, CONCEPT)
83
+ patient_ehr_events = None
83
84
  for domain_table_name in domain_table_list:
84
- if domain_table_name != MEASUREMENT:
85
- domain_tables.append(
86
- preprocess_domain_table(
87
- spark,
88
- input_folder,
89
- domain_table_name,
90
- with_drug_rollup=with_drug_rollup,
91
- )
92
- )
85
+ domain_table = preprocess_domain_table(
86
+ spark=spark,
87
+ input_folder=input_folder,
88
+ domain_table_name=domain_table_name,
89
+ with_drug_rollup=with_drug_rollup
90
+ )
91
+ ehr_events = extract_events_by_domain(
92
+ domain_table,
93
+ spark=spark,
94
+ concept=concept,
95
+ aggregate_by_hour=aggregate_by_hour,
96
+ refresh=refresh_measurement,
97
+ persistence_folder=input_folder
98
+ )
99
+ if patient_ehr_events is None:
100
+ patient_ehr_events = ehr_events
101
+ else:
102
+ patient_ehr_events = patient_ehr_events.unionByName(ehr_events)
93
103
 
94
104
  visit_occurrence = preprocess_domain_table(spark, input_folder, VISIT_OCCURRENCE)
95
105
  visit_occurrence = visit_occurrence.select(
@@ -119,35 +129,17 @@ def main(
119
129
 
120
130
  death = preprocess_domain_table(spark, input_folder, DEATH) if include_death else None
121
131
 
122
- patient_events = join_domain_tables(domain_tables)
123
-
124
- if include_concept_list and patient_events:
125
- column_names = patient_events.schema.fieldNames()
132
+ if include_concept_list and patient_ehr_events:
126
133
  # Filter out concepts
127
- qualified_concepts = preprocess_domain_table(spark, input_folder, "qualified_concept_list").select(
128
- "standard_concept_id"
129
- )
130
-
131
- patient_events = patient_events.join(qualified_concepts, "standard_concept_id").select(column_names)
132
-
133
- # Process the measurement table if exists
134
- if MEASUREMENT in domain_table_list:
135
- processed_measurement = get_measurement_table(
136
- spark,
137
- input_folder,
138
- refresh=refresh_measurement,
139
- aggregate_by_hour=aggregate_by_hour,
134
+ qualified_concepts = preprocess_domain_table(spark, input_folder, "qualified_concept_list")
135
+ patient_ehr_events = patient_ehr_events.join(
136
+ qualified_concepts.select("standard_concept_id"), "standard_concept_id"
140
137
  )
141
- if patient_events:
142
- # Union all measurement records together with other domain records
143
- patient_events = patient_events.unionByName(processed_measurement)
144
- else:
145
- patient_events = processed_measurement
146
138
 
147
- patient_events = (
148
- patient_events.join(visit_occurrence_person, "visit_occurrence_id")
139
+ patient_ehr_events = (
140
+ patient_ehr_events.join(visit_occurrence_person, "visit_occurrence_id")
149
141
  .select(
150
- [patient_events[fieldName] for fieldName in patient_events.schema.fieldNames()]
142
+ [patient_ehr_events[fieldName] for fieldName in patient_ehr_events.schema.fieldNames()]
151
143
  + ["visit_concept_id", "age"]
152
144
  )
153
145
  .withColumn("cohort_member_id", F.col("person_id"))
@@ -156,25 +148,35 @@ def main(
156
148
  # Apply the age security measure
157
149
  # We only keep the patient records, whose corresponding age is less than 90
158
150
  if apply_age_filter:
159
- patient_events = patient_events.where(F.col("age") < 90)
151
+ patient_ehr_events = patient_ehr_events.where(F.col("age") < 90)
160
152
 
161
153
  if not continue_from_events:
162
- patient_events.write.mode("overwrite").parquet(os.path.join(output_folder, "all_patient_events"))
163
-
164
- patient_events = spark.read.parquet(os.path.join(output_folder, "all_patient_events"))
154
+ patient_ehr_events.write.mode("overwrite").parquet(os.path.join(output_folder, "all_patient_events"))
165
155
 
156
+ patient_ehr_events = spark.read.parquet(os.path.join(output_folder, "all_patient_events"))
166
157
  if should_construct_artificial_visits:
167
158
  # 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,
159
+ patient_ehr_events, visit_occurrence_person = construct_artificial_visits(
160
+ patient_ehr_events,
170
161
  visit_occurrence_person,
171
162
  spark=spark,
172
163
  persistence_folder=output_folder,
164
+ duplicate_records=duplicate_records
173
165
  )
166
+ # Update age if some of the ehr_records have been re-associated with the new visits
167
+ patient_ehr_events = patient_ehr_events.join(
168
+ person.select("person_id", "birth_datetime"),
169
+ "person_id",
170
+ ).join(
171
+ visit_occurrence_person.select("visit_occurrence_id", "visit_start_date"), "visit_occurrence_id"
172
+ ).withColumn(
173
+ "age",
174
+ F.ceil(F.months_between(F.col("visit_start_date"), F.col("birth_datetime")) / F.lit(12))
175
+ ).drop("visit_start_date", "birth_datetime")
174
176
 
175
177
  if is_new_patient_representation:
176
- sequence_data = create_sequence_data_with_att(
177
- patient_events,
178
+ patient_sequence_data = create_sequence_data_with_att(
179
+ patient_ehr_events,
178
180
  visit_occurrence_person,
179
181
  date_filter=date_filter,
180
182
  include_visit_type=include_visit_type,
@@ -190,8 +192,8 @@ def main(
190
192
  persistence_folder=output_folder,
191
193
  )
192
194
  else:
193
- sequence_data = create_sequence_data(
194
- patient_events,
195
+ patient_sequence_data = create_sequence_data(
196
+ patient_ehr_events,
195
197
  date_filter=date_filter,
196
198
  include_visit_type=include_visit_type,
197
199
  classic_bert_seq=is_classic_bert,
@@ -215,47 +217,33 @@ def main(
215
217
  )
216
218
  .distinct()
217
219
  )
218
- sequence_data = sequence_data.join(visit_occurrence, "person_id")
219
-
220
- if include_sequence_information_content:
221
- concept_df = patient_events.select("person_id", F.col("standard_concept_id").alias("concept_id"))
222
- concept_freq = (
223
- concept_df.groupBy("concept_id")
224
- .count()
225
- .withColumn("prob", F.col("count") / F.sum("count").over(Window.partitionBy()))
226
- .withColumn("ic", -F.log("prob"))
227
- )
228
-
229
- patient_ic_df = concept_df.join(concept_freq, "concept_id").groupby("person_id").agg(F.mean("ic").alias("ic"))
230
-
231
- sequence_data = sequence_data.join(patient_ic_df, "person_id")
220
+ patient_sequence_data = patient_sequence_data.join(visit_occurrence, "person_id")
232
221
 
233
222
  patient_splits_folder = os.path.join(input_folder, "patient_splits")
234
223
  if os.path.exists(patient_splits_folder):
235
224
  patient_splits = spark.read.parquet(patient_splits_folder)
236
225
  temp_folder = os.path.join(output_folder, "patient_sequence", "temp")
237
- sequence_data.join(
226
+ patient_sequence_data.join(
238
227
  patient_splits.select("person_id", "split"),
239
228
  "person_id"
240
229
  ).write.mode("overwrite").parquet(
241
230
  temp_folder
242
231
  )
243
- sequence_data = spark.read.parquet(temp_folder)
244
- sequence_data.where('split="train"').write.mode("overwrite").parquet(
232
+ patient_sequence_data = spark.read.parquet(temp_folder)
233
+ patient_sequence_data.where('split="train"').write.mode("overwrite").parquet(
245
234
  os.path.join(output_folder, "patient_sequence/train")
246
235
  )
247
- sequence_data.where('split="test"').write.mode("overwrite").parquet(
236
+ patient_sequence_data.where('split="test"').write.mode("overwrite").parquet(
248
237
  os.path.join(output_folder, "patient_sequence/test")
249
238
  )
250
239
  shutil.rmtree(temp_folder)
251
240
  else:
252
- sequence_data.write.mode("overwrite").parquet(os.path.join(output_folder, "patient_sequence"))
241
+ patient_sequence_data.write.mode("overwrite").parquet(os.path.join(output_folder, "patient_sequence"))
253
242
 
254
243
 
255
- if __name__ == "__main__":
256
- parser = argparse.ArgumentParser(description="Arguments for generate training data for Bert")
244
+ def create_argparser():
245
+ parser = argparse.ArgumentParser(description="Arguments for generate training data for CEHR-Bert/GPT")
257
246
  parser.add_argument(
258
- "-i",
259
247
  "--input_folder",
260
248
  dest="input_folder",
261
249
  action="store",
@@ -263,7 +251,6 @@ if __name__ == "__main__":
263
251
  required=True,
264
252
  )
265
253
  parser.add_argument(
266
- "-o",
267
254
  "--output_folder",
268
255
  dest="output_folder",
269
256
  action="store",
@@ -271,7 +258,6 @@ if __name__ == "__main__":
271
258
  required=True,
272
259
  )
273
260
  parser.add_argument(
274
- "-tc",
275
261
  "--domain_table_list",
276
262
  dest="domain_table_list",
277
263
  nargs="+",
@@ -369,8 +355,17 @@ if __name__ == "__main__":
369
355
  help="Indicate whether we should construct artificial visits for "
370
356
  "the problem list records that could occur years ahead",
371
357
  )
358
+ parser.add_argument(
359
+ "--duplicate_records",
360
+ dest="duplicate_records",
361
+ action="store_true",
362
+ help="Indicate whether we want to duplicate the problem list records when constructing artificial visits"
363
+ )
364
+ return parser
365
+
372
366
 
373
- ARGS = parser.parse_args()
367
+ if __name__ == "__main__":
368
+ ARGS = create_argparser().parse_args()
374
369
 
375
370
  # Enable logging
376
371
  add_console_logging()
@@ -399,4 +394,5 @@ if __name__ == "__main__":
399
394
  refresh_measurement=ARGS.refresh_measurement,
400
395
  aggregate_by_hour=ARGS.aggregate_by_hour,
401
396
  should_construct_artificial_visits=ARGS.should_construct_artificial_visits,
397
+ duplicate_records=ARGS.duplicate_records
402
398
  )
@@ -13,16 +13,17 @@ from pyspark.sql.window import Window
13
13
  from cehrbert_data.decorators import AttType
14
14
  from cehrbert_data.const.common import VISIT_OCCURRENCE
15
15
  from cehrbert_data.utils.spark_utils import (
16
- build_ancestry_table_for,
17
16
  create_concept_frequency_data,
18
- create_hierarchical_sequence_data,
19
17
  create_sequence_data,
20
18
  create_sequence_data_with_att,
21
19
  extract_ehr_records,
22
- get_descendant_concept_ids,
23
20
  preprocess_domain_table,
24
21
  construct_artificial_visits
25
22
  )
23
+ from cehrbert_data.utils.vocab_utils import (
24
+ build_ancestry_table_for,
25
+ get_descendant_concept_ids
26
+ )
26
27
  from cehrbert_data.utils.logging_utils import add_console_logging
27
28
 
28
29
  from ..cohorts.query_builder import ENTRY_COHORT, NEGATIVE_COHORT, QueryBuilder
@@ -300,7 +301,6 @@ class NestedCohortBuilder:
300
301
  aggregate_by_hour: bool = True,
301
302
  is_new_patient_representation: bool = False,
302
303
  gpt_patient_sequence: bool = False,
303
- is_hierarchical_bert: bool = False,
304
304
  classic_bert_seq: bool = False,
305
305
  is_first_time_outcome: bool = False,
306
306
  is_questionable_outcome_existed: bool = False,
@@ -318,6 +318,7 @@ class NestedCohortBuilder:
318
318
  meds_format: bool = False,
319
319
  cache_events: bool = False,
320
320
  should_construct_artificial_visits: bool = False,
321
+ duplicate_records: bool = False,
321
322
  ):
322
323
  self._cohort_name = cohort_name
323
324
  self._input_folder = input_folder
@@ -342,7 +343,6 @@ class NestedCohortBuilder:
342
343
  self._is_drug_roll_up_concept = is_drug_roll_up_concept
343
344
  self._is_new_patient_representation = is_new_patient_representation
344
345
  self._gpt_patient_sequence = gpt_patient_sequence
345
- self._is_hierarchical_bert = is_hierarchical_bert
346
346
  self._is_first_time_outcome = is_first_time_outcome
347
347
  self._is_remove_index_prediction_starts = is_remove_index_prediction_starts
348
348
  self._is_questionable_outcome_existed = is_questionable_outcome_existed
@@ -365,6 +365,7 @@ class NestedCohortBuilder:
365
365
  self._meds_format = meds_format
366
366
  self._cache_events = cache_events
367
367
  self._should_construct_artificial_visits = should_construct_artificial_visits
368
+ self._duplicate_records = duplicate_records
368
369
 
369
370
  self.get_logger().info(
370
371
  f"cohort_name: {cohort_name}\n"
@@ -386,7 +387,6 @@ class NestedCohortBuilder:
386
387
  f"is_drug_roll_up_concept: {is_drug_roll_up_concept}\n"
387
388
  f"is_new_patient_representation: {is_new_patient_representation}\n"
388
389
  f"gpt_patient_sequence: {gpt_patient_sequence}\n"
389
- f"is_hierarchical_bert: {is_hierarchical_bert}\n"
390
390
  f"is_first_time_outcome: {is_first_time_outcome}\n"
391
391
  f"is_questionable_outcome_existed: {is_questionable_outcome_existed}\n"
392
392
  f"is_remove_index_prediction_starts: {is_remove_index_prediction_starts}\n"
@@ -406,6 +406,7 @@ class NestedCohortBuilder:
406
406
  f"meds_format: {meds_format}\n"
407
407
  f"cache_events: {cache_events}\n"
408
408
  f"should_construct_artificial_visits: {should_construct_artificial_visits}\n"
409
+ f"duplicate_records: {duplicate_records}\n"
409
410
  )
410
411
 
411
412
  self.spark = SparkSession.builder.appName(f"Generate {self._cohort_name}").getOrCreate()
@@ -637,12 +638,36 @@ class NestedCohortBuilder:
637
638
  )
638
639
 
639
640
  if self._should_construct_artificial_visits:
641
+ person = self._dependency_dict[PERSON]
642
+ birthdate_udf = F.coalesce(
643
+ "birth_datetime",
644
+ F.concat("year_of_birth", F.lit("-01-01")).cast("timestamp"),
645
+ )
646
+ patient_demographic = person.select(
647
+ "person_id",
648
+ birthdate_udf.alias("birth_datetime"),
649
+ )
640
650
  ehr_records, visit_occurrence_with_artificial_visits = construct_artificial_visits(
641
651
  ehr_records,
642
652
  self._dependency_dict[VISIT_OCCURRENCE],
643
653
  spark=self.spark if self._cache_events else None,
644
654
  persistence_folder=self._output_data_folder if self._cache_events else None,
655
+ duplicate_records=self._duplicate_records
645
656
  )
657
+
658
+ # Update age if some of the ehr_records have been re-associated with the new visits
659
+ ehr_records = ehr_records.join(
660
+ patient_demographic,
661
+ "person_id"
662
+ ).join(
663
+ visit_occurrence_with_artificial_visits.select(
664
+ "visit_occurrence_id", "visit_start_date"
665
+ ), "visit_occurrence_id"
666
+ ).withColumn(
667
+ "age",
668
+ F.ceil(F.months_between(F.col("visit_start_date"), F.col("birth_datetime")) / F.lit(12))
669
+ ).drop("visit_start_date", "birth_datetime")
670
+
646
671
  # Refresh the dependency
647
672
  self._dependency_dict[VISIT_OCCURRENCE] = visit_occurrence_with_artificial_visits
648
673
 
@@ -672,8 +697,9 @@ class NestedCohortBuilder:
672
697
  else:
673
698
  if self._is_observation_window_unbounded:
674
699
  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")
700
+ F.col("ehr.datetime")
701
+ <= F.expr(
702
+ f"cohort.index_date - INTERVAL {self._hold_off_window} DAYS + INTERVAL 0.1 SECOND")
677
703
  )
678
704
  else:
679
705
  record_window_filter = F.col("ehr.datetime").between(
@@ -692,14 +718,6 @@ class NestedCohortBuilder:
692
718
  [F.col("ehr." + field_name) for field_name in ehr_records.schema.fieldNames()]
693
719
  )
694
720
 
695
- if self._is_hierarchical_bert:
696
- return create_hierarchical_sequence_data(
697
- person=self._dependency_dict[PERSON],
698
- visit_occurrence=self._dependency_dict[VISIT_OCCURRENCE],
699
- patient_events=cohort_ehr_records,
700
- allow_measurement_only=self._allow_measurement_only,
701
- )
702
-
703
721
  if self._is_feature_concept_frequency:
704
722
  return create_concept_frequency_data(cohort_ehr_records, None)
705
723
 
@@ -840,7 +858,6 @@ def create_prediction_cohort(
840
858
  aggregate_by_hour=spark_args.aggregate_by_hour,
841
859
  is_new_patient_representation=spark_args.is_new_patient_representation,
842
860
  gpt_patient_sequence=spark_args.gpt_patient_sequence,
843
- is_hierarchical_bert=spark_args.is_hierarchical_bert,
844
861
  classic_bert_seq=spark_args.classic_bert_seq,
845
862
  is_first_time_outcome=spark_args.is_first_time_outcome,
846
863
  # If the outcome negative query exists, that means we need to remove those questionable
@@ -862,4 +879,5 @@ def create_prediction_cohort(
862
879
  meds_format=spark_args.meds_format,
863
880
  cache_events=spark_args.cache_events,
864
881
  should_construct_artificial_visits=spark_args.should_construct_artificial_visits,
882
+ duplicate_records=spark_args.duplicate_records,
865
883
  ).build()
@@ -7,6 +7,8 @@ DEVICE_EXPOSURE = "device_exposure"
7
7
  OBSERVATION = "observation"
8
8
  MEASUREMENT = "measurement"
9
9
  PROCESSED_MEASUREMENT = "processed_measurement"
10
+ PROCESSED_OBSERVATION = "processed_observation"
11
+ PROCESSED_DEVICE = "processed_device"
10
12
  CATEGORICAL_MEASUREMENT = "categorical_measurement"
11
13
  OBSERVATION_PERIOD = "observation_period"
12
14
  DEATH = "death"
@@ -1,8 +1,7 @@
1
- import os.path
2
-
3
1
  from ..const.common import (
4
2
  MEASUREMENT,
5
3
  CATEGORICAL_MEASUREMENT,
4
+ OBSERVATION,
6
5
  )
7
6
  from pyspark.sql import SparkSession, DataFrame, functions as F, Window as W, types as T
8
7
 
@@ -127,6 +126,13 @@ class ClinicalEventDecorator(PatientEventDecorator):
127
126
  ).otherwise(F.col("datetime"))
128
127
  )
129
128
 
129
+ outpatient_datetime_udf = (
130
+ F.when(
131
+ F.col("is_inpatient") == 0,
132
+ F.to_timestamp("date")
133
+ ).otherwise(F.col("datetime"))
134
+ )
135
+
130
136
  patient_events = (
131
137
  patient_events.join(visits, ["cohort_member_id", "visit_occurrence_id"])
132
138
  .withColumn("datetime", F.to_timestamp("datetime"))
@@ -136,6 +142,7 @@ class ClinicalEventDecorator(PatientEventDecorator):
136
142
  .withColumn("visit_end_datetime", F.expr("visit_end_datetime - INTERVAL 1 MINUTE"))
137
143
  .withColumn("date", bound_medical_event_date)
138
144
  .withColumn("datetime", bound_medical_event_datetime)
145
+ .withColumn("datetime", outpatient_datetime_udf)
139
146
  .withColumn("concept_order", concept_order_udf)
140
147
  .withColumn("visit_concept_order", visit_concept_order_udf)
141
148
  .drop("is_inpatient", "visit_end_date", "visit_end_datetime")
@@ -154,9 +161,9 @@ class ClinicalEventDecorator(PatientEventDecorator):
154
161
  # Create the concept_value_mask field to indicate whether domain values should be skipped
155
162
  # As of now only measurement has values, so other domains would be skipped.
156
163
  patient_events = patient_events.withColumn(
157
- "concept_value_mask", (F.col("domain").isin(MEASUREMENT, CATEGORICAL_MEASUREMENT)).cast("int")
164
+ "concept_value_mask", (F.col("number_as_value").isNotNull() | F.col("concept_as_value").isNotNull()).cast("int")
158
165
  ).withColumn(
159
- "is_numeric_type", (F.col("domain") == MEASUREMENT).cast("int")
166
+ "is_numeric_type", F.col("number_as_value").isNotNull().cast("int")
160
167
  ).withColumn(
161
168
  "mlm_skip_value",
162
169
  (F.col("domain").isin([MEASUREMENT, CATEGORICAL_MEASUREMENT])).cast("int"),
@@ -13,10 +13,12 @@ from cehrbert_data.decorators import AttType
13
13
  from cehrbert_data.utils.spark_parse_args import create_spark_args
14
14
  from cehrbert_data.utils.spark_utils import (
15
15
  extract_ehr_records,
16
+ preprocess_domain_table,
16
17
  construct_artificial_visits,
17
18
  create_sequence_data_with_att,
18
19
  create_concept_frequency_data,
19
20
  )
21
+ from cehrbert_data.const.common import PERSON, VISIT_OCCURRENCE
20
22
 
21
23
 
22
24
  class PredictionType(Enum):
@@ -128,15 +130,41 @@ def main(args):
128
130
  ehr_records.write.mode("overwrite").parquet(ehr_records_temp_folder)
129
131
  ehr_records = spark.read.parquet(ehr_records_temp_folder)
130
132
 
131
- visit_occurrence = spark.read.parquet(os.path.join(args.input_folder, "visit_occurrence"))
133
+
134
+ birthdate_udf = f.coalesce(
135
+ "birth_datetime",
136
+ f.concat("year_of_birth", f.lit("-01-01")).cast("timestamp"),
137
+ )
138
+
139
+ person = preprocess_domain_table(spark, args.input_folder, PERSON)
140
+ patient_demographic = person.select(
141
+ "person_id",
142
+ birthdate_udf.alias("birth_datetime"),
143
+ "race_concept_id",
144
+ "gender_concept_id",
145
+ )
146
+
147
+
148
+ visit_occurrence = preprocess_domain_table(spark, args.input_folder, VISIT_OCCURRENCE)
132
149
 
133
150
  if args.should_construct_artificial_visits:
134
151
  ehr_records, visit_occurrence = construct_artificial_visits(
135
152
  ehr_records,
136
153
  visit_occurrence=visit_occurrence,
137
154
  spark=spark,
138
- persistence_folder = str(os.path.join(args.output_folder, args.cohort_name))
155
+ persistence_folder = str(os.path.join(args.output_folder, args.cohort_name)),
156
+ duplicate_records=args.duplicate_records
139
157
  )
158
+ # Update age if some of the ehr_records have been re-associated with the new visits
159
+ ehr_records = ehr_records.join(
160
+ patient_demographic.select("person_id", "birth_datetime"),
161
+ "person_id",
162
+ ).join(
163
+ visit_occurrence.select("visit_occurrence_id", "visit_start_date"), "visit_occurrence_id"
164
+ ).withColumn(
165
+ "age",
166
+ f.ceil(f.months_between(f.col("visit_start_date"), f.col("birth_datetime")) / f.lit(12))
167
+ ).drop("visit_start_date", "birth_datetime")
140
168
 
141
169
  cohort_visit_occurrence = visit_occurrence.join(
142
170
  cohort.select("person_id", "cohort_member_id", "index_date"),
@@ -157,22 +185,6 @@ def main(args):
157
185
  f.col("visit_end_date").cast(t.TimestampType()),
158
186
  f.col("visit_start_datetime")
159
187
  ).cast(t.TimestampType())
160
- ).where(
161
- f.col("visit_start_datetime") <=
162
- f.expr(f"index_date - INTERVAL {args.hold_off_window} DAYS + INTERVAL 0.1 SECOND")
163
- )
164
-
165
-
166
- birthdate_udf = f.coalesce(
167
- "birth_datetime",
168
- f.concat("year_of_birth", f.lit("-01-01")).cast("timestamp"),
169
- )
170
- person = spark.read.parquet(os.path.join(args.input_folder, "person"))
171
- patient_demographic = person.select(
172
- "person_id",
173
- birthdate_udf.alias("birth_datetime"),
174
- "race_concept_id",
175
- "gender_concept_id",
176
188
  )
177
189
 
178
190
  age_udf = f.ceil(f.months_between(f.col("visit_start_date"), f.col("birth_datetime")) / f.lit(12))
@@ -416,4 +416,10 @@ def create_spark_args(parse: bool = True):
416
416
  help="Indicate whether we should construct artificial visits for "
417
417
  "the problem list records that could occur years ahead",
418
418
  )
419
+ parser.add_argument(
420
+ "--duplicate_records",
421
+ dest="duplicate_records",
422
+ action="store_true",
423
+ help="Indicate whether we want to duplicate the problem list records when constructing artificial visits"
424
+ )
419
425
  return parser.parse_args() if parse else parser