OHDSI / Capr

Cohort definition Application Programming in R
https://ohdsi.github.io/Capr
Apache License 2.0
15 stars 11 forks source link

How to select first exposure? #61

Closed schuemie closed 1 year ago

schuemie commented 1 year ago

I'd like to pick the first exposure to a drug, and then require a washout period. I realized that this:

sitagliptinNewUsers <- cohort(
  entry = entry(
    drug(sitagliptin),
    primaryCriteriaLimit = "First",
    observationWindow = continuousObservation(priorDays = 365)
  ),
  attrition = attrition(
    "prior T2DM" = withAll(
      atLeast(1, condition(t2dm), duringInterval(eventStarts(-Inf, 0)))
    )
  ),
  exit = exit(endStrategy = drugExit(sitagliptin, persistenceWindow = 30, surveillanceWindow = 0))
)

actually first applies the 365 washout, then picks the first occurrence. So if someone was exposed on days 1 and 366 relative to observation_period_start_date, the resulting cohort will have an entry for day 366. Instead, this person should not be included in the cohort (first exposure is on day 1, there is less than 365 days of prior observation on day 1).

I tried using firstOccurrence():

sitagliptinNewUsers <- cohort(
  entry = entry(
    drug(sitagliptin, firstOccurrence()),
    observationWindow = continuousObservation(priorDays = 365)
  ),
  attrition = attrition(
    "prior T2DM" = withAll(
      atLeast(1, condition(t2dm), duringInterval(eventStarts(-Inf, 0)))
    )
  ),
  exit = exit(endStrategy = drugExit(sitagliptin, persistenceWindow = 30, surveillanceWindow = 0))
)

but then as.json() throws an error:

as.json(sitagliptinNewUsers)
# Error in `purrr::map()`:
# i In index: 1.
# Caused by error in `purrr::map()`:
# i In index: 1.
# Caused by error in `as.list.default()`:
# ! no method for coercing this S4 class to a vector
mdlavallee92 commented 1 year ago

This is a minor bug. I forgot to add a coercion method for the logical attribute.

Adding the code and unit test.

General HADES question @schuemie , for hot fixes like this can I push straight to main or should this be done via a PR and new release?

ablack3 commented 1 year ago

You can push the fix to develop. I think all commits to main need to be releases (for Hades repos). For this you could create a patch release.

ablack3 commented 1 year ago

develop branch looks good to me.

# devtools::install_github("OHDSI/Capr", "develop")

library(Capr)

sitagliptin <- cs(descendants(1580747))
t2dm <- cs(descendants(201826))

sitagliptinNewUsers <- cohort(
  entry = entry(
    drug(sitagliptin, firstOccurrence()),
    observationWindow = continuousObservation(priorDays = 365)
  ),
  attrition = attrition(
    "prior T2DM" = withAll(
      atLeast(1, condition(t2dm), duringInterval(eventStarts(-Inf, 0)))
    )
  ),
  exit = exit(endStrategy = drugExit(sitagliptin, persistenceWindow = 30, surveillanceWindow = 0))
)
#> Loading required namespace: testthat

cat(as.json(sitagliptinNewUsers))
#> {
#>   "ConceptSets": [
#>     {
#>       "id": 0,
#>       "name": "",
#>       "expression": {
#>         "items": [
#>           {
#>             "concept": {
#>               "CONCEPT_ID": 1580747,
#>               "CONCEPT_NAME": "",
#>               "STANDARD_CONCEPT": "",
#>               "STANDARD_CONCEPT_CAPTION": "",
#>               "INVALID_REASON": "",
#>               "INVALID_REASON_CAPTION": "",
#>               "CONCEPT_CODE": "",
#>               "DOMAIN_ID": "",
#>               "VOCABULARY_ID": "",
#>               "CONCEPT_CLASS_ID": ""
#>             },
#>             "isExcluded": false,
#>             "includeDescendants": true,
#>             "includeMapped": false
#>           }
#>         ]
#>       }
#>     },
#>     {
#>       "id": 1,
#>       "name": "",
#>       "expression": {
#>         "items": [
#>           {
#>             "concept": {
#>               "CONCEPT_ID": 201826,
#>               "CONCEPT_NAME": "",
#>               "STANDARD_CONCEPT": "",
#>               "STANDARD_CONCEPT_CAPTION": "",
#>               "INVALID_REASON": "",
#>               "INVALID_REASON_CAPTION": "",
#>               "CONCEPT_CODE": "",
#>               "DOMAIN_ID": "",
#>               "VOCABULARY_ID": "",
#>               "CONCEPT_CLASS_ID": ""
#>             },
#>             "isExcluded": false,
#>             "includeDescendants": true,
#>             "includeMapped": false
#>           }
#>         ]
#>       }
#>     }
#>   ],
#>   "PrimaryCriteria": {
#>     "CriteriaList": [
#>       {
#>         "DrugExposure": {
#>           "CodesetId": 0,
#>           "First": true
#>         }
#>       }
#>     ],
#>     "ObservationWindow": {
#>       "PriorDays": 365,
#>       "PostDays": 0
#>     },
#>     "PrimaryCriteriaLimit": {
#>       "Type": "First"
#>     }
#>   },
#>   "QualifiedLimit": {
#>     "Type": "First"
#>   },
#>   "ExpressionLimit": {
#>     "Type": "First"
#>   },
#>   "InclusionRules": [
#>     {
#>       "name": "prior T2DM",
#>       "expression": {
#>         "Type": "ALL",
#>         "CriteriaList": [
#>           {
#>             "Criteria": {
#>               "ConditionOccurrence": {
#>                 "CodesetId": 1
#>               }
#>             },
#>             "StartWindow": {
#>               "Start": {
#>                 "Coeff": -1
#>               },
#>               "End": {
#>                 "Days": 0,
#>                 "Coeff": -1
#>               },
#>               "UseIndexEnd": false,
#>               "UseEventEnd": false
#>             },
#>             "Occurrence": {
#>               "Type": 2,
#>               "Count": 1
#>             }
#>           }
#>         ],
#>         "DemographicCriteriaList": [],
#>         "Groups": []
#>       }
#>     }
#>   ],
#>   "EndStrategy": {
#>     "CustomEra": {
#>       "DrugCodesetId": 0,
#>       "GapDays": 30,
#>       "Offset": 0
#>     }
#>   },
#>   "CensoringCriteria": [],
#>   "CollapseSettings": {
#>     "CollapseType": "ERA",
#>     "EraPad": 0
#>   },
#>   "CensorWindow": {},
#>   "cdmVersionRange": ">=5.0.0"
#> }

# you can also use the compile generic function
cat(generics::compile(sitagliptinNewUsers))
#> {"ConceptSets":[{"id":0,"name":"","expression":{"items":[{"concept":{"CONCEPT_ID":1580747,"CONCEPT_NAME":"","STANDARD_CONCEPT":"","STANDARD_CONCEPT_CAPTION":"","INVALID_REASON":"","INVALID_REASON_CAPTION":"","CONCEPT_CODE":"","DOMAIN_ID":"","VOCABULARY_ID":"","CONCEPT_CLASS_ID":""},"isExcluded":false,"includeDescendants":true,"includeMapped":false}]}},{"id":1,"name":"","expression":{"items":[{"concept":{"CONCEPT_ID":201826,"CONCEPT_NAME":"","STANDARD_CONCEPT":"","STANDARD_CONCEPT_CAPTION":"","INVALID_REASON":"","INVALID_REASON_CAPTION":"","CONCEPT_CODE":"","DOMAIN_ID":"","VOCABULARY_ID":"","CONCEPT_CLASS_ID":""},"isExcluded":false,"includeDescendants":true,"includeMapped":false}]}}],"PrimaryCriteria":{"CriteriaList":[{"DrugExposure":{"CodesetId":0,"First":true}}],"ObservationWindow":{"PriorDays":365,"PostDays":0},"PrimaryCriteriaLimit":{"Type":"First"}},"QualifiedLimit":{"Type":"First"},"ExpressionLimit":{"Type":"First"},"InclusionRules":[{"name":"prior T2DM","expression":{"Type":"ALL","CriteriaList":[{"Criteria":{"ConditionOccurrence":{"CodesetId":1}},"StartWindow":{"Start":{"Coeff":-1},"End":{"Days":0,"Coeff":-1},"UseIndexEnd":false,"UseEventEnd":false},"Occurrence":{"Type":2,"Count":1}}],"DemographicCriteriaList":[],"Groups":[]}}],"EndStrategy":{"CustomEra":{"DrugCodesetId":0,"GapDays":30,"Offset":0}},"CensoringCriteria":[],"CollapseSettings":{"CollapseType":"ERA","EraPad":0},"CensorWindow":{},"cdmVersionRange":">=5.0.0"}

Created on 2023-04-04 with reprex v2.0.2

schuemie commented 1 year ago

@mdlavallee92 : Every push to main needs to be a new release. This doesn't have to be a lot of work:

mdlavallee92 commented 1 year ago

@schuemie done as a new release! Thanks for the clarification so I know what to do in the future.

Thanks for posting your issues! I really appreciate them 😎