OHDSI Home | Forums | Wiki | Github

Concept Ancestor Descendants-of-Descendants

Hi there -

I’m running into an issue with the concept_ancestor table but I’m not sure if it’s an issue with my understanding of how the table functions or some other issue.

My understanding is that if I find an “ancestor” concept then it should have all descendants concepts, including those of descendants further downstream. So in this example if I were to look for descendants of Heart disease (concept_id = 321588) then this should be a “superset” of concepts that would include any descendants of Atrial fibrillation (concept_id = 313217). Is that accurate?

Assuming this is the case, I tested my understanding with this SQL query where I expect for the result to be num_descendant_not_in_parent = 0.

with parent_descendents as (
    select distinct ca.descendant_concept_id
    from vocab.concept_ancestor ca
    where ca.ancestor_concept_id = 321588 -- heart disease
),
child_descendents as (
    select distinct ca.descendant_concept_id
    from vocab.concept_ancestor ca
    where ca.ancestor_concept_id in (select descendant_concept_id from parent_descendents)        
),
all_descendents as (
    select descendant_concept_id
    from parent_descendents
    union
    select descendant_concept_id
    from child_descendents
),
compare_descendents as (
    select 
        all_descendents.descendant_concept_id as all_descendent_concept_id,
        pd.descendant_concept_id as parent_descendent_concept_id,
        cd.descendant_concept_id as child_descendent_concept_id
    from 
        all_descendents
    left join 
        parent_descendents pd
            on all_descendents.descendant_concept_id = pd.descendant_concept_id
    left join 
        child_descendents cd
            on all_descendents.descendant_concept_id = cd.descendant_concept_id     
)
select
    count(distinct all_descendent_concept_id) as num_descendant_not_in_parent
from 
    compare_descendents
where 
    parent_descendent_concept_id is null
;

That code does return 0 as I expected. However, I have another situation where this is not true. I’m interested in finding all surfactant drug exposures and am having a problem with this same “jump” to descendants.

For example, in our Atlas instance I see that lung surfactants - combinations; inhalant, parenteral (concept_id = 21603535) has DRC and does show the records correctly. For example: Lung Surfactants → calfactant (concept_id = 19089891) → calfactant 35 MG/ML (concept_id = 19089893) and so on.

But when I try to run a query like this I get 0 results:

select *
from cdm.drug_exposure
where drug_concept_id in (
    select ca.descendant_concept_id
    from vocab.concept_ancestor ca 
    where ca.ancestor_concept_id = 21603535 -- lung surfactants - combinations; inhalant, parenteral    
)
;

So I tried this ancestor concept using the above SQL to compare descendent pairs:

with parent_descendents as (
    select distinct ca.descendant_concept_id
    from vocab.concept_ancestor ca
    where ca.ancestor_concept_id = 21603535 -- lung surfactants - combinations; inhalant, parenteral
),
child_descendents as (
    select distinct ca.descendant_concept_id
    from vocab.concept_ancestor ca
    where ca.ancestor_concept_id in (select descendant_concept_id from parent_descendents)        
),
all_descendents as (
    select descendant_concept_id
    from parent_descendents
    union
    select descendant_concept_id
    from child_descendents
),
compare_descendents as (
    select 
        all_descendents.descendant_concept_id as all_descendent_concept_id,
        pd.descendant_concept_id as parent_descendent_concept_id,
        cd.descendant_concept_id as child_descendent_concept_id
    from 
        all_descendents
    left join 
        parent_descendents pd
            on all_descendents.descendant_concept_id = pd.descendant_concept_id
    left join 
        child_descendents cd
            on all_descendents.descendant_concept_id = cd.descendant_concept_id     
)
select
    count(distinct all_descendent_concept_id) as num_descendant_not_in_parent
from 
    compare_descendents
where 
    parent_descendent_concept_id is null
;

But that query returned num_descendant_not_in_parent = 543.

So I’m wondering: am I using the concept_ancestor table correctly? If not, how should I use it? What is the “right” way to find all surfactant drugs?

Thanks!
Will

@wtroddy:

Yes, that is the ATC anomaly in the CONCEPT_ANCESTOR table.

Usually, you can rely on the fact that the children of your children are you grandchildren. With ATC, it doesn’t work like that, because those concepts are defined using a number of attributes, which don’t allow a hierarchical mapping into RxNorm (they are picked up much lower in the hierarchy there). Therefore, we created an exception from that rule.

In your case, ATC concept 21603535 has the attributes “certain surfactant ingredient”, “non enteral dose form” and “combination”. Which means, not all children of the ingredients are also grandchildren of this particular ATC. When we had that, we had a gigantic permutational explosion of wrong descendants.

So, don’t double jump. Your query against the DRUG_EXPOSURE table is right on the money. Or are you expecting something else to show up that is not there?

@Christian_Reich - thanks for the response and for linking the other thread RE: ATC concepts. These are more complicated than I previously understand and appreciate the communities work to sort out these nuances!

Yes, I’m expecting results that aren’t being returned by that query against the DRUG_EXPOSURE table. My query against DRUG_EXPOSURE gives 0 records, but I believe it should be returning descendants. However, I see the previous thread had some discussion about how Atlas represents these relationships so this may be related to that topic.

In my Atlas instance - when I browse concept 21603535 I see a value in the DRC field. Drilling down, these are under the children trees for calfactant (concept 19089891) and poractant alfa (concept 19091377). If I continue down to grandchildren I find that the calfactant branch has RC for concept_id = 19066701 (calfactant 35 MG/ML Intratracheal Suspension).

If I query my CDM instance for this specific concept_id, I do get results. For example, this query returns records:

select *
from cdm_inc1017_chop_ehr.drug_exposure
where drug_concept_id = 19066701 
;

It looks like the CONCEPT_ANCESTOR table has these relationships:

  1. lung surfactants - combinations; inhalant, parenteral (concept 21603535) is an ancestor of calfactant (concept 19089891).
  2. calfactant (concept 19089891) is an ancestor of calfactant 35 MG/ML Intratracheal Suspension (concept 19066701).
  3. But 21603535 is not an ancestor of 19066701.

So I’m wondering, why does the query using the CONCEPT_ANCESTOR table and the ancestor concept 21603535 not return the same results from this above query but Atlas does show the DRC?

Thanks again for help and patience with this!
Will

@wtroddy:

Because 19066701 calfactant 35 MG/ML Intratracheal Suspension is not a combination, but a single ingredient drug. Therefore it is not a descendant.

As I said, the child of the child may not be the grandchild. The ingredient 19089891 is a child of the ATC class 21603535. And the drug 19066701 is the child of the ingredient. But it is not a grandchild.

the DRC for the ATC Class must be counted from other drugs that are indeed parenteral combination drugs containing those sulfactant ingredients.

Got it - after re-reading your last responses and this that makes sense. I think it’s a bit confusing that Atlas’ record count don’t line up cleanly with this but I’ll leave that for another discussion.

My last question - what is the “right” way to get all surfactant drug exposures? Is it preferred to find specific ingredients and then those descendants? For example using CONCEPT_ANCESTOR where the ancestor is either calfactant OR poractant alfa etc. where I build a list of acceptable ingredients? Or is there a better way to about this?

Thanks again for the help!

That’s exactly what you want to do: Pick the ingredients in RxNorm or RxNorm Extension and then pull the descendants. Atlas can do that for you.

If, however, you need fixed combinations you can’t do that, because the ingredients are stand alone. You would have to use Clinical Drug Forms and use their descendants. There is a Concept Class Multiple Ingredients, but they are not yet connected to the actual drugs, except through the ingredients, which means you lose the combination.

Gotcha, thanks!

This sounds like an important caveat, I’m not sure I’ve run into this situation yet but will be on the lookout.

Thanks again!

t