Medicine Classification
This is based on DrugBaseEvaluationClassification but cleaned up towards final inclusion into the mw maintenance code.
Creating The Ingredient/Moiety Classes
Active Moiety (Misrf)
CREATE TABLE mw."mc_Misrf" AS
WITH ObjectTrait AS (
SELECT DISTINCT
i.scoperInternalId AS objectId,
m.playerInternalId AS traitValue
FROM mw.Role_ i
INNER JOIN mw.Role_ m ON(m.scoperInternalId = i.playerInternalId)
WHERE i.classCode = 'ACTI' AND m.classCode = 'ACTM'
), ObjectTraitCount AS (
SELECT objectId,
COUNT(1) AS traitCount
FROM ObjectTrait
GROUP BY objectId
), ObjectTraitWithCount AS (
SELECT * FROM ObjectTrait
INNER JOIN ObjectTraitCount
USING(objectId)
), EquivalenceRelation AS (
SELECT s1.objectId AS object1Id,
s2.objectId AS object2Id
FROM ObjectTraitWithCount s1
INNER JOIN ObjectTraitWithCount s2
USING(traitValue, traitCount)
GROUP BY s1.objectId, s2.objectId
HAVING COUNT(1) = MAX(traitCount)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId
FROM EquivalenceRelation
GROUP BY object1Id
15763 record(s) affected
Active Ingredient (mIsrf)
CREATE TABLE mw."mc_mIsrf" AS
WITH ObjectTrait AS (
SELECT scoperInternalId AS objectId,
playerInternalId AS traitValue
FROM mw.Role_
WHERE classCode = 'ACTI'
), ObjectTraitCount AS (
SELECT objectId,
COUNT(1) AS traitCount
FROM ObjectTrait
GROUP BY objectId
), ObjectTraitWithCount AS (
SELECT * FROM ObjectTrait
INNER JOIN ObjectTraitCount
USING(objectId)
), EquivalenceRelation AS (
SELECT s1.objectId AS object1Id,
s2.objectId AS object2Id
FROM ObjectTraitWithCount s1
INNER JOIN ObjectTraitWithCount s2
USING(traitValue, traitCount)
GROUP BY s1.objectId, s2.objectId
HAVING COUNT(1) = MAX(traitCount)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId
FROM EquivalenceRelation
GROUP BY object1Id
15763 record(s) affected
ActiveIngredient? and Strength (mISrf)
CREATE TABLE mw."mc_mISrf" AS
WITH ObjectTrait AS (
SELECT scoperInternalId AS objectId,
playerInternalId AS traitValue1,
quantity_numeratorNumber/quantity_denominatorNumber AS traitValue2,
CASE WHEN quantity_numeratorUnit IS NULL THEN '1' ELSE quantity_numeratorUnit END
||CASE WHEN quantity_denominatorUnit <> '1' THEN '/'||quantity_denominatorUnit ELSE '' END
AS traitValue3
FROM mw.Role_
WHERE classCode = 'ACTI'
), ObjectTraitCount AS (
SELECT objectId,
COUNT(1) AS traitCount
FROM ObjectTrait
GROUP BY objectId
), ObjectTraitWithCount AS (
SELECT * FROM ObjectTrait
INNER JOIN ObjectTraitCount
USING(objectId)
), EquivalenceRelation AS (
SELECT s1.objectId AS object1Id,
s2.objectId AS object2Id
FROM ObjectTraitWithCount s1
INNER JOIN ObjectTraitWithCount s2
USING(traitValue1, traitValue2, traitValue3, traitCount)
GROUP BY s1.objectId, s2.objectId
HAVING COUNT(1) = MAX(traitCount)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId
FROM EquivalenceRelation
GROUP BY object1Id
15654 record(s) affected
We seem to be missing a few strengths. Never mind.
Building the Refined Classes
Route
Active Moieties and Route (MisRf?)
CREATE TABLE mw."mc_MisRf" AS
WITH BroaderClassMember AS (
SELECT classId, memberId FROM mw."mc_Misrf"
), DifferentiatingProperty AS (
SELECT r.playerInternalId AS memberId, a.routeCode_code AS value
FROM mw.Role_ r
INNER JOIN mw.Participation p ON(p.roleInternalId = r.internalId)
INNER JOIN mw.Act a ON(
a.internalId = p.actInternalId
AND a.classCode='SBADM'
AND a.moodCode='DEF'
AND a.routeCode_codeSystem = '2.16.840.1.113883.3.26.1.1')
), ClassMemberDifference AS (
SELECT c.classId AS genusId,
memberId AS objectId,
p.value AS difference
FROM BroaderClassMember c
INNER JOIN DifferentiatingProperty p
USING(memberId)
), EquivalenceRelation AS (
SELECT m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, difference
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(genusId, difference)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId, difference
FROM EquivalenceRelation
GROUP BY object1Id, difference
15685 record(s) affected
Wonder how this can be simplified and automated better.
Active Ingredients and Route (mIsRf)
CREATE TABLE mw."mc_mIsRf" AS
WITH BroaderClassMember AS (
SELECT classId, memberId FROM mw."mc_mIsrf"
), DifferentiatingProperty AS (
SELECT r.playerInternalId AS memberId, a.routeCode_code AS value
FROM mw.Role_ r
INNER JOIN mw.Participation p ON(p.roleInternalId = r.internalId)
INNER JOIN mw.Act a ON(
a.internalId = p.actInternalId
AND a.classCode='SBADM'
AND a.moodCode='DEF'
AND a.routeCode_codeSystem = '2.16.840.1.113883.3.26.1.1')
), ClassMemberDifference AS (
SELECT c.classId AS genusId,
memberId AS objectId,
p.value AS difference
FROM BroaderClassMember c
INNER JOIN DifferentiatingProperty p
USING(memberId)
), EquivalenceRelation AS (
SELECT m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, difference
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(genusId, difference)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId, difference
FROM EquivalenceRelation
GROUP BY object1Id, difference
15685 record(s) affected
Active Ingredient with Strength and Route (mISRf)
CREATE TABLE mw."mc_mISRf" AS
WITH BroaderClassMember AS (
SELECT classId, memberId FROM mw."mc_mISrf"
), DifferentiatingProperty AS (
SELECT r.playerInternalId AS memberId, a.routeCode_code AS value
FROM mw.Role_ r
INNER JOIN mw.Participation p ON(p.roleInternalId = r.internalId)
INNER JOIN mw.Act a ON(
a.internalId = p.actInternalId
AND a.classCode='SBADM'
AND a.moodCode='DEF'
AND a.routeCode_codeSystem = '2.16.840.1.113883.3.26.1.1')
), ClassMemberDifference AS (
SELECT c.classId AS genusId,
memberId AS objectId,
p.value AS difference
FROM BroaderClassMember c
INNER JOIN DifferentiatingProperty p
USING(memberId)
), EquivalenceRelation AS (
SELECT m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, difference
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(genusId, difference)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId, difference
FROM EquivalenceRelation
GROUP BY object1Id, difference
15577 record(s) affected
Form
Active Moiety, Form (MisrF)
CREATE TABLE mw."mc_MisrF" AS
WITH BroaderClassMember AS (
SELECT classId, memberId FROM mw."mc_Misrf"
), DifferentiatingProperty AS (
SELECT internalId as memberId, formCode_code as value
FROM mw.Entity
WHERE determinerCode = 'KIND'
AND formCode_codeSystem = '2.16.840.1.113883.3.26.1.1'
), ClassMemberDifference AS (
SELECT c.classId AS genusId,
memberId AS objectId,
p.value AS difference
FROM BroaderClassMember c
INNER JOIN DifferentiatingProperty p
USING(memberId)
), EquivalenceRelation AS (
SELECT m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, difference
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(genusId, difference)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId, difference
FROM EquivalenceRelation
GROUP BY object1Id, difference
15763 record(s) affected
Active Ingredient, Form (mIsrF)
CREATE TABLE mw."mc_mIsrF" AS
WITH BroaderClassMember AS (
SELECT classId, memberId FROM mw."mc_mIsrf"
), DifferentiatingProperty AS (
SELECT internalId as memberId, formCode_code as value
FROM mw.Entity
WHERE determinerCode = 'KIND'
AND formCode_codeSystem = '2.16.840.1.113883.3.26.1.1'
), ClassMemberDifference AS (
SELECT c.classId AS genusId,
memberId AS objectId,
p.value AS difference
FROM BroaderClassMember c
INNER JOIN DifferentiatingProperty p
USING(memberId)
), EquivalenceRelation AS (
SELECT m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, difference
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(genusId, difference)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId, difference
FROM EquivalenceRelation
GROUP BY object1Id, difference
15763 record(s) affected
Active Ingredient, Strength, Form (mISrF)
CREATE TABLE mw."mc_mISrF" AS
WITH BroaderClassMember AS (
SELECT classId, memberId FROM mw."mc_mISrf"
), DifferentiatingProperty AS (
SELECT internalId as memberId, formCode_code as value
FROM mw.Entity
WHERE determinerCode = 'KIND'
AND formCode_codeSystem = '2.16.840.1.113883.3.26.1.1'
), ClassMemberDifference AS (
SELECT c.classId AS genusId,
memberId AS objectId,
p.value AS difference
FROM BroaderClassMember c
INNER JOIN DifferentiatingProperty p
USING(memberId)
), EquivalenceRelation AS (
SELECT m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, difference
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(genusId, difference)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId, difference
FROM EquivalenceRelation
GROUP BY object1Id, difference
15654 record(s) affected
Now Combine the Classes
CREATE TABLE mw."mc_MisRF" AS
WITH BroaderClass1Member AS (
SELECT classId, memberId FROM mw."mc_MisRf"
), BroaderClass2Member AS (
SELECT classId, memberId FROM mw."mc_MisrF"
), CombinedClassMember AS (
SELECT c1.classId AS class1Id,
c2.classId AS class2Id,
memberId
FROM BroaderClass1Member c1
INNER JOIN BroaderClass2Member c2 USING(memberId)
) SELECT MIN(m2.memberId) AS classId,
m1.memberId AS memberId,
class1Id AS genus1Id,
class2Id AS genus2Id
FROM CombinedClassMember m1
INNER JOIN CombinedClassMember m2 USING(class1Id, class2Id)
GROUP BY class1Id, class2Id, m1.memberId
CREATE TABLE mw."mc_mIsRF" AS
WITH BroaderClass1Member AS (
SELECT classId, memberId FROM mw."mc_mIsRf"
), BroaderClass2Member AS (
SELECT classId, memberId FROM mw."mc_mIsrF"
), CombinedClassMember AS (
SELECT c1.classId AS class1Id,
c2.classId AS class2Id,
memberId
FROM BroaderClass1Member c1
INNER JOIN BroaderClass2Member c2 USING(memberId)
)
SELECT MIN(m2.memberId) AS classId,
m1.memberId AS memberId,
class1Id AS genus1Id,
class2Id AS genus2Id
FROM CombinedClassMember m1
INNER JOIN CombinedClassMember m2 USING(class1Id, class2Id)
GROUP BY class1Id, class2Id, m1.memberId
CREATE TABLE mw."mc_mISRF" AS
WITH BroaderClass1Member AS (
SELECT classId, memberId FROM mw."mc_mISrF"
), BroaderClass2Member AS (
SELECT classId, memberId FROM mw."mc_mISRf"
), CombinedClassMember AS (
SELECT c1.classId AS class1Id,
c2.classId AS class2Id,
memberId
FROM BroaderClass1Member c1
INNER JOIN BroaderClass2Member c2 USING(memberId)
)
SELECT MIN(m2.memberId) AS classId,
m1.memberId AS memberId,
class1Id AS genus1Id,
class2Id AS genus2Id
FROM CombinedClassMember m1
INNER JOIN CombinedClassMember m2 USING(class1Id, class2Id)
GROUP BY class1Id, class2Id, m1.memberId
Putting it all Together
Finally, and the hardest part is to keep all these names right.
DROP TABLE mw.mc_EquivalenceClassMember GO CREATE TABLE mw.mc_EquivalenceClassMember AS SELECT 'Misrf' AS category, classId, memberId, NULL AS difference FROM mw."mc_Misrf" UNION ALL SELECT 'mIsrf', classId, memberId, NULL FROM mw."mc_mIsrf" UNION ALL SELECT 'mISrf', classId, memberId, NULL FROM mw."mc_mISrf" UNION ALL SELECT 'MisRf', classId, memberId, difference FROM mw."mc_MisRf" UNION ALL SELECT 'MisrF', classId, memberId, difference FROM mw."mc_MisrF" UNION ALL SELECT 'mIsRf', classId, memberId, difference FROM mw."mc_mIsRf" UNION ALL SELECT 'mIsrF', classId, memberId, difference FROM mw."mc_mIsrF" UNION ALL SELECT 'mIsRf', classId, memberId, difference FROM mw."mc_mISRf" UNION ALL SELECT 'mIsrF', classId, memberId, difference FROM mw."mc_mISrF" UNION ALL SELECT 'MisRF', classId, memberId, NULL FROM mw."mc_MisRF" UNION ALL SELECT 'mIsRF', classId, memberId, NULL FROM mw."mc_mIsRF" UNION ALL SELECT 'mISRF', classId, memberId, NULL FROM mw."mc_mISRF"
188254 record(s) affected
SELECT category, count(1) classCount,
sum(memberCount) totalMemberCount,
min(memberCount), median(memberCount),
round(avg(memberCount),3), round(stddev(memberCount),3), max(memberCount)
FROM (
SELECT category, classId, count(1) memberCount
FROM mw.mc_EquivalenceClassMember
GROUP BY category, classId
) GROUP BY category
ORDER BY count(1) ASC
| CATEGORY | CLASSES | MEMBERS | |||||
| TOTAL | MIN | MED | AVG | STDDEV | MAX | ||
| Misrf | 2344 | 15763 | 1 | 2 | 6.725 | 13.682 | 185 |
| mIsrf | 2672 | 15763 | 1 | 2 | 5.899 | 12.206 | 185 |
| MisR-f | 2705 | 15810 | 1 | 2 | 5.845 | 11.701 | 174 |
| MisrF- | 2937 | 15757 | 1 | 2 | 5.365 | 9.962 | 150 |
| MisRf? | 2975 | 16202 | 1 | 2 | 5.446 | 10.998 | 174 |
| MisrR-F- | 3199 | 15764 | 1 | 2 | 4.928 | 9.027 | 150 |
| MisrF | 4255 | 15763 | 1 | 2 | 3.705 | 6.217 | 111 |
| MisRF | 4425 | 15814 | 1 | 2 | 3.574 | 6.003 | 111 |
| mIsRF | 4650 | 15798 | 1 | 2 | 3.397 | 5.662 | 111 |
| mISrf | 7158 | 15654 | 1 | 1 | 2.187 | 3.111 | 54 |
| mIsRf | 7397 | 32295 | 1 | 2 | 4.366 | 8.866 | 186 |
| mIsrF | 8372 | 31417 | 1 | 2 | 3.753 | 6.315 | 138 |
| mISRF | 8389 | 15640 | 1 | 1 | 1.864 | 2.444 | 49 |
The checks at the end of DrugBaseEvaluationCombiningClasses all pass fine. So we are good now. The only thing that's not quite right is that we missed some drugs which were not classified. But instead of fixing this we should rather exclude those cases.
One for All
Here is a single query that does all of the above.
DROP TABLE mc_class
GO
CREATE TABLE mc_class AS
WITH ObjectTrait AS (
SELECT DISTINCT 'Mis' AS category,
i.scoperInternalId AS objectId,
m.playerInternalId AS traitValue,
0 AS traitValue2,
' ' AS traitValue3
FROM mw.Role_ i
INNER JOIN mw.Role_ m ON(m.scoperInternalId = i.playerInternalId)
WHERE i.classCode = 'ACTI' AND m.classCode = 'ACTM'
UNION ALL
SELECT 'mIs' AS category,
scoperInternalId AS objectId,
playerInternalId AS traitValue,
0 AS traitValue2,
' ' AS traitValue3
FROM mw.Role_
WHERE classCode = 'ACTI'
UNION ALL
SELECT 'mIS' AS category,
scoperInternalId AS objectId,
playerInternalId AS traitValue1,
quantity_numeratorNumber/quantity_denominatorNumber AS traitValue2,
CASE WHEN quantity_numeratorUnit IS NULL THEN '1' ELSE quantity_numeratorUnit END
||CASE WHEN quantity_denominatorUnit <> '1' THEN '/'||quantity_denominatorUnit ELSE '' END
AS traitValue3
FROM mw.Role_
WHERE classCode = 'ACTI'
), ObjectTraitCount AS (
SELECT category, objectId, COUNT(1) AS traitCount
FROM ObjectTrait
GROUP BY category, objectId
), ObjectTraitWithCount AS (
SELECT * FROM ObjectTrait
INNER JOIN ObjectTraitCount
USING(category, objectId)
), EquivalenceRelation AS (
SELECT category,
s1.objectId AS object1Id,
s2.objectId AS object2Id,
COUNT(1) AS traitCount
FROM ObjectTraitWithCount s1
INNER JOIN ObjectTraitWithCount s2
USING(category, traitCount, traitValue, traitValue2, traitValue3)
GROUP BY category, s1.objectId, s2.objectId
HAVING COUNT(1) = MAX(traitCount)
), BroaderClassMember AS (
SELECT category,
MIN(object2Id) AS classId,
object1Id AS memberId,
traitCount
FROM EquivalenceRelation
GROUP BY category, traitCount, object1Id
), DifferentiatingProperty AS (
SELECT 'r' AS category1, 'f' AS category2, memberId, ' ' as value1, ' ' as value2
FROM BroaderClassMember
GROUP BY memberId
UNION ALL
SELECT 'r' AS category1, 'F' AS category2, internalId as memberId, ' ' as value1, formCode_code as value2
FROM mw.Entity
WHERE determinerCode = 'KIND'
AND formCode_codeSystem = '2.16.840.1.113883.3.26.1.1'
UNION ALL
SELECT 'R' AS category1, 'f' AS category2, r.playerInternalId AS memberId, a.routeCode_code AS value1, ' ' as value2
FROM mw.Role_ r
INNER JOIN mw.Participation p ON(p.roleInternalId = r.internalId)
INNER JOIN mw.Act a ON(
a.internalId = p.actInternalId
AND a.classCode='SBADM'
AND a.moodCode='DEF'
AND a.routeCode_codeSystem = '2.16.840.1.113883.3.26.1.1')
), ClassMemberDifference AS (
SELECT category, category1, category2,
c.classId AS genusId,
memberId AS objectId,
traitCount,
p.value1 AS difference1,
p.value2 AS difference2
FROM BroaderClassMember c
INNER JOIN DifferentiatingProperty p
USING(memberId)
), EquivalenceRelation2 AS (
SELECT category, category1, category2,
m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, traitCount, difference1, difference2
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(category, category1, category2, genusId, traitCount, difference1, difference2)
), BroaderClassMember1 AS (
SELECT category, category1, category2,
MIN(object2Id) AS classId, object1Id AS memberId,
traitCount, difference1, difference2
FROM EquivalenceRelation2
GROUP BY category, category1, category2, object1Id, traitCount, difference1, difference2
), CombinedClassMember AS (
SELECT category, c1.category1, c2.category2,
c1.classId AS class1Id,
c2.classId AS class2Id,
memberId, traitCount, c1.difference1, c2.difference2
FROM BroaderClassMember1 c1
INNER JOIN BroaderClassMember1 c2 USING(category, memberId, traitCount)
) SELECT category, category1, category2,
MIN(m2.memberId) AS classId,
m1.memberId AS memberId,
class1Id AS genus1Id,
class2Id AS genus2Id,
traitCount, difference1, difference2
FROM CombinedClassMember m1
INNER JOIN CombinedClassMember m2 USING(category, category1, category2, class1Id, class2Id, traitCount, difference1, difference2)
GROUP BY category, category1, category2, class1Id, class2Id, m1.memberId, traitCount, difference1, difference2
Now here is the generalization discussed below, but with the sophistication that combination formulation will be with strength and simple drugs as moiety only.
WITH BroaderClassMember AS (
SELECT category, category1, category2, classId, memberId,
traitCount, difference1, difference2
FROM mc_class
WHERE ((traitCount=1 AND category='Mis') OR (traitCount>1 AND category='mIS'))
AND category1='R' AND category2='F'
), DifferenceGeneralization1 AS (
SELECT source_code AS difference1,
target_code AS difference1Class
FROM mw.Concept_Relationship
WHERE type_code='ISA'
AND source_codeSystem = '2.16.840.1.113883.3.26.1.1'
AND target_codeSystem = '1.3.6.1.4.1.12009.4.1.12'
), DifferenceGeneralization2 AS (
SELECT source_code AS difference2,
target_code AS difference2Class
FROM mw.Concept_Relationship
WHERE type_code='ISA'
AND source_codeSystem = '2.16.840.1.113883.3.26.1.1'
AND target_codeSystem = '1.3.6.1.4.1.12009.4.1.13'
), ClassMemberDifference AS (
SELECT 'MIS' AS category, category1||'+' AS category1, category2||'+' AS category2,
c.classId AS genusId,
memberId AS objectId,
traitCount,
g1.difference1Class AS difference1,
g2.difference2Class AS difference2
FROM BroaderClassMember c
INNER JOIN DifferenceGeneralization1 g1 USING(difference1)
INNER JOIN DifferenceGeneralization2 g2 USING(difference2)
), EquivalenceRelation AS (
SELECT category, category1, category2,
m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, traitCount, difference1, difference2
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(category, category1, category2, genusId, traitCount, difference1, difference2)
) SELECT category, category1, category2,
MIN(object2Id) AS classId,
object1Id AS memberId,
traitCount, difference1, difference2
FROM EquivalenceRelation
GROUP BY category, category1, category2, object1Id, traitCount, difference1, difference2
So What?
The reason for this excercise is so that we can reduce clutter in the medicine name selection. We can create the drug classes as generic formulations, again in SPL format, and adding act relationships to their members. We do not need all those many classifications, only those named generic classes which we like to include on the medication selection.
For this, we need to add one more higher level class, which would combine forms into only the variances:
- liquid
- solid
and the route into
- oral
- parenteral
- topical
Not sure what to do with rectal, vaginal, urethral, and also otic and nasal. Those can sometimes be considered topical and other times systemic. Buccal and sublingual. Sublingual definitely is systemic.
So, we define for route
- systemic
- oral
- parenteral (incl. sublingual, rectal, transdermal)
- injection (incl. infusion, anything requiring a percutanous access) - however, the injection property is derived from the dose form.
- topical (incl. nasal, otic)
- special (anything that requires special skills and procedures, e.g. intraspinal, intraocular, etc.)
For form we use:
- fluid - all non-solids non-countables, including gas and powder
- liquid - incl. solutions, suspensions, including solid susbtances reconstituted into liquids for administration
- countable fluid - Any fluid, including liquid, which can still be dosed by counting. Includes metered sprays or tablets for solution.
- solid - all solid forms, hard or soft, as long as they maintain integrity in handling and can therefore be counted.
With just this classification we can create the following combinations
The notion of "countable fluid" is not well understood though. Hence we probably need to translate it to better understood concepts.
- systemic (only for NEC)
- oral (only for NEC)
- oral solid
- oral liquid
- oral countable fluid (liquid each?)
- oral metered dose
- parenteral (only for NEC)
- parenteral injectable liquid
- parenteral liquid
- parenteral solid
- parenteral countable fluid (liquid each?)
- parenteral metered dose
- oral (only for NEC)
- topical (only for NEC)
- topical solid
- topical liquid
- topical drops
- special (all special items lumped under one category)
Before we defined this ourselves, the NCI Thesaurus was considered as a source for a rational and usable classification of dose form and route. However, the categorizations appear to have many flaws and effectively do not support our purposes for very rough classes used for prescribing. In the end we will have to continue applying our own best guess categorization.
Forming The Hierarchies Based on Property Value Abstractions
Form Code Class
DROP TABLE mw."mc_MisrF-"
GO
CREATE TABLE mw."mc_MisrF-" AS
WITH BroaderClassMember AS (
SELECT classId, memberId FROM mw."mc_Misrf"
), DifferentiatingProperty AS (
SELECT internalId as memberId, formCode_code as value
FROM mw.Entity
WHERE determinerCode = 'KIND'
AND formCode_codeSystem = '2.16.840.1.113883.3.26.1.1'
), PropertyGeneralization AS (
SELECT source_code AS value,
target_code AS valueClass
FROM mw.Concept_Relationship
WHERE type_code='ISA'
AND source_codeSystem = '2.16.840.1.113883.3.26.1.1'
AND target_codeSystem = '1.3.6.1.4.1.12009.4.1.13'
), ClassMemberDifference AS (
SELECT c.classId AS genusId,
memberId AS objectId,
g.valueClass AS difference
FROM BroaderClassMember c
INNER JOIN DifferentiatingProperty p
INNER JOIN PropertyGeneralization g USING(value)
USING(memberId)
), EquivalenceRelation AS (
SELECT m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, difference
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(genusId, difference)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId, difference
FROM EquivalenceRelation
GROUP BY object1Id, difference
Route Code Class
DROP TABLE mw."mc_MisR-f"
GO
CREATE TABLE mw."mc_MisR-f" AS
WITH BroaderClassMember AS (
SELECT classId, memberId FROM mw."mc_Misrf"
), DifferentiatingProperty AS (
SELECT r.playerInternalId AS memberId, a.routeCode_code AS value
FROM mw.Role_ r
INNER JOIN mw.Participation p ON(p.roleInternalId = r.internalId)
INNER JOIN mw.Act a ON(
a.internalId = p.actInternalId
AND a.classCode='SBADM'
AND a.moodCode='DEF'
AND a.routeCode_codeSystem = '2.16.840.1.113883.3.26.1.1')
), PropertyGeneralization AS (
SELECT source_code AS value,
target_code AS valueClass
FROM mw.Concept_Relationship
WHERE type_code='ISA'
AND source_codeSystem = '2.16.840.1.113883.3.26.1.1'
AND target_codeSystem = '1.3.6.1.4.1.12009.4.1.12'
), ClassMemberDifference AS (
SELECT c.classId AS genusId,
memberId AS objectId,
g.valueClass AS difference
FROM BroaderClassMember c
INNER JOIN DifferentiatingProperty p
INNER JOIN PropertyGeneralization g USING(value)
USING(memberId)
), EquivalenceRelation AS (
SELECT m1.objectId AS object1Id,
m2.objectId AS object2Id,
genusId, difference
FROM ClassMemberDifference m1
INNER JOIN ClassMemberDifference m2
USING(genusId, difference)
) SELECT object1Id AS memberId,
MIN(object2Id) AS classId, difference
FROM EquivalenceRelation
GROUP BY object1Id, difference
And combine
DROP TABLE mw."mc_MisR-F-"
GO
CREATE TABLE mw."mc_MisR-F-" AS
WITH BroaderClass1Member AS (
SELECT classId, memberId, difference FROM mw."mc_MisrF-"
), BroaderClass2Member AS (
SELECT classId, memberId, difference FROM mw."mc_MisR-f"
), CombinedClassMember AS (
SELECT c1.classId AS class1Id, c1.difference AS difference1,
c2.classId AS class2Id, c2.difference AS difference2,
memberId
FROM BroaderClass1Member c1
INNER JOIN BroaderClass2Member c2 USING(memberId)
)
SELECT MIN(m2.memberId) AS classId,
m1.memberId AS memberId,
class1Id AS genus1Id, difference1,
class2Id AS genus2Id, difference2
FROM CombinedClassMember m1
INNER JOIN CombinedClassMember m2 USING(class1Id, class2Id, difference1, difference2)
GROUP BY class1Id, class2Id, difference1, difference2, m1.memberId
Add To Our Central Class Membership Relation
ALTER TABLE mw.mc_EquivalenceClassMember
MODIFY category VARCHAR2(16)
ADD (difference2 VARCHAR2(64))
GO
DELETE FROM mw.mc_EquivalenceClassMember WHERE category IN ('MisrF-', 'MisR-f', 'MisR-F-')
GO
INSERT INTO mw.mc_EquivalenceClassMember
SELECT 'MisrF-', classId, memberId, difference, NULL FROM mw."mc_MisrF-"
UNION ALL
SELECT 'MisR-f', classId, memberId, NULL, difference FROM mw."mc_MisR-f"
UNION ALL
SELECT 'MisR-F-', classId, memberId, difference1, difference2 FROM mw."mc_MisR-F-"
| ROUTE | FORM | COUNT |
| oral | solid | 7422 |
| oral | liquid | 2664 |
| parenteral | injectable liquid | 2466 |
| topical | liquid | 1646 |
| topical | solid | 811 |
| parenteral | solid | 315 |
| parenteral | liquid | 180 |
| special | injectable liquid | 102 |
| topical | fluid | 57 |
| parenteral | fluid | 38 |
| parenteral | metered dose | 24 |
| topical | injectable liquid | 21 |
| special | liquid | 14 |
| topical | metered dose | 13 |
| topical | drops | 9 |
| special | solid | 7 |
| oral | fluid | 6 |
| oral | metered dose | 3 |
| special | fluid | 3 |
| oral | liquid each | 2 |
| oral | injectable liquid | 1 |
The last one is rather funny, but otherwise it seems pretty nice.
Create Generic Names
Two forms:
- "amoxicillin oral solid"
- "tylenol (acetaminophen) oral liquid"
Strength would never be in the list. It could be found when choosing the specific formulation.
The second form cannot be made from the classes. The only reason for a brand name should be the original innovators brand name, not some generic manufacturer. It would be nice to have a source of original innovators for each drug class. That way we can also have "Tylenol III (acetaminophen and codein) oral solid", etc.
Check out NewGenericNames for an updated approach.
DROP TABLE mw.mc_Class
GO
CREATE TABLE mw.mc_Class AS
WITH IngredientMoieties AS (
SELECT i.scoperInternalId parentId, n.trivialname AS name
FROM mw.Role_ i
INNER JOIN mw.Role_ m ON(m.scoperInternalId = i.playerInternalId AND m.classCode = 'ACTM')
INNER JOIN mw.Entity_name n ON(n.entityInternalId = m.playerInternalId)
WHERE i.classCode='ACTI'
), GenericNameAssembly AS (
SELECT parentId, STRINGJOIN(' and '||name) AS items
FROM IngredientMoieties
GROUP BY parentId
), GenericName AS (
SELECT parentId AS objectId, SUBSTR(items,LENGTH(' and ')) as name
FROM GenericNameAssembly
), NDC_SCD AS (
SELECT substr(ndc,1,5)||'-'||substr(ndc,6,4) AS ndc, scd_rxcui
FROM rxn_ndc_cleanup
), NDC_SBD AS (
SELECT ndc, scd_rxcui, rxcui, str
FROM NDC_SCD
INNER JOIN umls.RXNREL ON(rxcui2 = scd_rxcui AND RELA='has_tradename')
INNER JOIN umls.RXNCONSO ON(rxcui = rxcui1)
WHERE SAB='RXNORM' AND TTY='SBD' AND LAT='ENG'
), NDC_TRADENAME1 AS (
SELECT r.*,
INSTR(str,'[',-1) AS tradename_start,
INSTR(str,']',-1) AS tradename_end
FROM NDC_SBD r
), NDC_TRADENAME2 AS (
SELECT DISTINCT
ndc,
LOWER(SUBSTR(str, tradename_start+1, tradename_end-tradename_start-1)) AS tradename
FROM NDC_TRADENAME1 r
), NDC_TRADENAME AS (
SELECT ndc, count(1), SUBSTR(STRINGJOIN(' or '||tradename),LENGTH(' or ')+1) AS tradenames
FROM NDC_TRADENAME2
GROUP BY NDC
HAVING count(1) < 3
), RawClass AS (
SELECT DISTINCT classId, difference, difference2
FROM mw.mc_EquivalenceClassMember
WHERE category = 'MisR-F-'
), ClassStage AS (
SELECT classId, n.name, t.tradenames, difference2 AS route, difference AS form,
REPLACE(c.code_code,CHR(14844051),'-') AS representative_code,
c.code_codeSystem AS representative_codeSystem
FROM RawClass
INNER JOIN mw.Entity c ON(c.internalId = classId)
INNER JOIN GenericName n ON(n.objectId = classId)
LEFT OUTER JOIN NDC_TRADENAME t ON(t.ndc = c.code_code)
ORDER BY classId
) SELECT * FROM ClassStage
3223 record(s) affected
Nice and compact, this is ready to hand over to db2xml!
DROP TABLE mw.mc_ClassIngredientMoiety
GO
CREATE TABLE mw.mc_ClassIngredientMoiety AS
SELECT i.scoperInternalId classId, n.trivialname AS name, e.code_code, e.code_codeSystem
FROM mw.Role_ i
INNER JOIN mw.Role_ m ON(m.scoperInternalId = i.playerInternalId AND m.classCode = 'ACTM')
INNER JOIN mw.Entity_name n ON(n.entityInternalId = m.playerInternalId)
INNER JOIN mw.Entity e ON(e.internalId = m.playerInternalId)
WHERE i.classCode='ACTI'
GO
CREATE INDEX mw.mc_ClassIngredientMoiety_idx ON mw.mc_ClassIngredientMoiety(classId)
GO
DROP TABLE mw.mc_ClassMember
GO
CREATE TABLE mw.mc_ClassMember AS
SELECT classId, memberId, code_code, code_codeSystem
FROM mw.mc_EquivalenceClassMember
INNER JOIN mw.Entity ON(internalId = memberId)
WHERE category = 'MisR-F-'
GO
CREATE INDEX mw.mc_ClassMember_idx ON mw.mc_ClassMember(classId)
![(please configure the [header_logo] section in trac.ini)](/mw/chrome/site/logo100.png)