From 9badd2a0258af510fe25eb9d730eb83e8291cdcb Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 10:43:09 -0700 Subject: [PATCH 001/112] adding metadata tracking doc --- resources/pheknowlator_source_metadata.xlsx | Bin 0 -> 12711 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/pheknowlator_source_metadata.xlsx diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1342a0ed5f700a4d949f6fab73370e46f5a64c53 GIT binary patch literal 12711 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 oU# Date: Tue, 7 Dec 2021 10:56:53 -0700 Subject: [PATCH 002/112] adding chemical-disease metadata --- resources/pheknowlator_source_metadata.xlsx | Bin 12711 -> 12919 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 1342a0ed5f700a4d949f6fab73370e46f5a64c53..27d0eefe4d132edfb075fa197958b2714e1f38cd 100644 GIT binary patch delta 6816 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 KA{n#)v-w|!6rlS6 delta 6631 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 lfCEmz&p`EeXlNk;0A9!dz_afAZ-xoi Date: Tue, 7 Dec 2021 11:36:26 -0700 Subject: [PATCH 003/112] adding chemical-phenotype metadata --- resources/pheknowlator_source_metadata.xlsx | Bin 12919 -> 13138 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 27d0eefe4d132edfb075fa197958b2714e1f38cd..588a738e7fc7d69c24873f46322225351b8d722d 100644 GIT binary patch delta 5677 zcmZ8_1yCHyvi9P(xLa@y4#9#GY;lL+!QEXJhoFIFad!#s5Zql82yVeOxIfPQ|9Yq1 zo$BhE>gnp5nznEH)@;_Ih5-ek@FizI1HyvBY@-K&R-uNr+stYHv{;UZ!qMv>j1$il zmfv)(r-nAHW-7Iw=JgNWoS%=6bNAh+*sI%4X?sbQNd}RSZWKBg3CJ!AJ4^(G0965o3Sx>% z1t*)+1BpPThGBt}qcVac%zmz8=~{=B(vbU~^?1|j>L%XMF}N!-&*p2KI}9}MV~R(? z@UQ*B-}}&3rq)1NfyAiSQevdnsf#s&@Tp-hWY{`>jVYZhWePmaGoqP`^~q*$;`C*& zG)SE~)6JJA4DnX`0M^A*CYuV9oASkIUz=I;#xx@uOSIERE0GRdc0l?yt#xq49fw9% zc&LVas$q-L*?{jEW)hiwT!7fDk??D9oEs9Jvzwb@(;NzKUl6_{kKx3#dFg$LiQr=% znbyg9_Ke~yJwxMs6Xti;JNjAHG47s<6zf<6isX!ot04TkC^z3-y@(D`COvJj-*x`x z$nFKDQV$i?)Zg^MKH-Fg#hXPKR|hQUG2H|F&ZfQuj~B8i9osw}h8w(-pFe7$vHqH# zZgjQ<)>VB~>5$OJ*SWu1Yxy&PWNH=4n>nA}Uhgd!Zva)ehN8TJ;|)LlQsejU#2br3ZpEVo z2gk3mvYO6@JV>8mpZhEGdOR;S1m#0E$DN#vNt}VBu5;rami&sl4mZ+TtwsTLbN5z$ z={N#nLFI@XRgxbp9#{J;T9+61t|arqo6DjeUk!1*SBPkg-n^H08X z`uPn@Q#}`;H@DlBI?rRJyLesj0qU24XK1MFk25&02mpX5A%umR7;;R60?y#T3qNBx z5!LC$G|t_T%+pL~(JQhpPxjaUI7q9-Y0)%Cn{~79X-9c7Z$Jx09wK_myzCU2yxOIF ze>L2QT|UW)LLK8Zp+R-17}o{X!;mH-mOl#CD&G|laS3s(TJOp@g$8iwO|rsD*t`5_ zY)T8wGtpx!L(%B7^-^bM1($`%bf72}2(Ug;>xMbwndcSe53-u%Pnnd~;l;4yEz21$ z6XbhKvCca-<#P%v*10+*9!3~&8-sWQb)rFw1rc9pfA2V`zgJwq-xv9o#N{^aG;l8e zz!UJINzat3B@N{TxGes9`uxIA7Lk-CTYa4qMrB=kU*JS~7ZbIv1;%L=tlhHI`75pC z5iHPtWyTOi_8RA~*Y}%47dR~R6={ZF5wYNRB9K5gX)@)vRT8@6>&B5am2pHCbD?+I zHgTFCEeb|;NoP<1awi}A;PC8qly;^rh8(iAGH3+9z~Kxzs7gc_>`bofUSAcx021b3 zA!mu9be6P@i>pBN0rx<@+lXAwPI@C$SZtt~YYF}Jy<-`&HLpxO?GR-kCI1EBoTZ} z4exb^jpa)>6Q&9L2;Yy9U}z8-D0<;xRBv|G8@~_EZri=IzaRtvUS9$Ku?#EVISwfE*gxCsspLi2S?m5=%RA?-c$RhhuKH_7&fYw@lkx4BEpB7o zaU79fZLT9$SkWZ$`N$=L{hQ$7yYql~5}w-vaWJ-gPwe$TuRs)_f($!u`sn`f z;uXiOATDugl|;I}FkN1Syp<-XMzR+{OQJDIB{f%!K~CjG5&uFc3&jclJRnbchl$T# zzz1V!Zt1v2m)`SbityF>v#$#&Mp{DncKda>I;pZ@FiF|PIxAi!xZwIBw{^#VgRCh- z1ngLoiaz2y%9VUdOng6QOz>TuU~e;^!&q*dY~!0a!g*A}r?x%jq_a}u`OY57o+b2N zcph1tHt}5fkbKZ(gP26+_p9ywGn?!>1ZTA-vc#HIOz$%@+s>!RkxGwEt>xWhX-!w# zL>67<7@t^ACKt3$1zMzXufE%u%jcmsFEG6AEh6qTsoefrARW;LvvAFj6+EvzNq=SI z)oXQpGld-|3fJO;)JZ-!wSbD0!VIZpMysYm&(c6rC_TQ$T%gwp zzxoMLZD>b;!@374BTXi6BAlk#j)FNcf^7;mF54o|FGi&$k1$egMup(&VlgmK8XQOK z!Uk&po^PN3_$lSQJRP=!6c6k8+c}6i zpF(WX6qb!mAvbjhImSD-1@C=Ffb4*G#2)I0T~PHfG@DhM=MbC;8x^6gmq)f?GzOUp z8wa=au=FfVyuBbA?z@;LSk={Xu>ar>Y_*xNWW3rvu1wCi`(71PlEMr1dLW=9Crt-> zaipUI$1TY3)4vn;>Edg%dXtnDGPT(KKGXOM`(;@o&Q>dh+&ZcvH&Dx5!}KuhwU(P* zh9{k0FjfQSGTAnTzh7T>3laWLLy&k{VXB&w z_n23E6On(HR1l-p=a~jLHNq48pJ57cXWvV<>%LTPvqPrH?ZD2~YrMm70DuM%0Kj|8 zB`-U6S1S`2b2D`}7i$MgS2izuyJPKThh=^&fA(oG(Z0DsZX=mv#so=IG`&*OAaB!5 zVS79xYph-Vk5bw{u~E-=ao^F%R43(wU=dK^zY1qk30UrCnhZKp6b1^Dj5VgiTRw{e zN~y_R8?)eZz^9Ie2Jpt@>YTWrH))4w5FJaVa$+mp#cD^Dg0ox?7=iW;k|TCTC8bt$ zGtyi#MiaaZW#RQmsX4>ZvG@-xd{7`O_ENV}b7j5^L?l2ie-EC-n&?=$dmj$ zpeRU#b_u5W+c%zm_MkQlJg%ilaS%A^aUyYUGvH^@1BdeHG#+)aafmqwKl*o68yv

Pj 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 delta 5440 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 L6NV}8U#tHE8fg#3 From 1a2b7dc2606d079ead40798060f7025f1bb79ee7 Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 11:39:30 -0700 Subject: [PATCH 004/112] formatting update --- resources/pheknowlator_source_metadata.xlsx | Bin 13138 -> 13072 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 588a738e7fc7d69c24873f46322225351b8d722d..58ec3f132c954afd111a3d1e5dc9c96c2c066fe3 100644 GIT binary patch delta 5288 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 H{^$HJf7A41 delta 5306 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 From ba2f78303393da73974e0ef5d3e6707381471819 Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 11:41:09 -0700 Subject: [PATCH 005/112] updating chemical-disease and chemical-phenotype filtering --- resources/resource_info.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 37b8c0f5..d5ab11c1 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -1,10 +1,10 @@ -chemical-disease|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|5;!=;''|None +chemical-disease|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None chemical-gene|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|http://www.ncbi.nlm.nih.gov/gene/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt|9;affects;not in x|6;==;Homo sapiens::5;.startswith('gene'); chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Biological Process chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Cellular Component chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Molecular Function chemical-pathway|;CHEBI_;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|None|None|5;==;Homo sapiens -chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|5;!=;''|None +chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|9;affects;not in x|6;==;Homo sapiens::5;.startswith('protein'); disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|None gene-disease|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|10;>=;1.0|None From 271f40e282faf8d88ecb89d78374b21a7e3cf372 Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 11:47:44 -0700 Subject: [PATCH 006/112] adding back chemical-rna edges --- resources/edge_source_list.txt | 1 + resources/resource_info.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/resources/edge_source_list.txt b/resources/edge_source_list.txt index 3f9c0d16..bfbbcbad 100644 --- a/resources/edge_source_list.txt +++ b/resources/edge_source_list.txt @@ -6,6 +6,7 @@ chemical-gomf, https://storage.googleapis.com/pheknowlator/current_build/data/or chemical-pathway, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/ChEBI2Reactome_All_Levels.txt chemical-phenotype, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chemicals_diseases.tsv chemical-protein, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_gene_ixns.tsv +chemical-rna, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_gene_ixns.tsv disease-phenotype, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/phenotype.hpoa gene-disease, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/curated_gene_disease_associations.tsv gene-gene, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt diff --git a/resources/resource_info.txt b/resources/resource_info.txt index d5ab11c1..20608a2a 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -6,6 +6,7 @@ chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/ chemical-pathway|;CHEBI_;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|None|None|5;==;Homo sapiens chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|9;affects;not in x|6;==;Homo sapiens::5;.startswith('protein'); +chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|9;activity;not in x::9;affects;not in x::9;reaction;not in x|6;==;Homo sapiens::5;.startswith('mRNA'); disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|None gene-disease|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|10;>=;1.0|None gene-gene|;;|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None From ce5745373597cec086950165441e0dde53fe8598 Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 12:11:20 -0700 Subject: [PATCH 007/112] adding chemical-gene metadata --- resources/pheknowlator_source_metadata.xlsx | Bin 13072 -> 17884 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 58ec3f132c954afd111a3d1e5dc9c96c2c066fe3..b27859e52f145902fd24e147f3bcbd213f137546 100644 GIT binary patch delta 11950 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 XZ Date: Tue, 7 Dec 2021 12:16:38 -0700 Subject: [PATCH 008/112] updating filtering criteria --- resources/resource_info.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 20608a2a..e3fabdf9 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -1,12 +1,12 @@ chemical-disease|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None -chemical-gene|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|http://www.ncbi.nlm.nih.gov/gene/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt|9;affects;not in x|6;==;Homo sapiens::5;.startswith('gene'); +chemical-gene|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|http://www.ncbi.nlm.nih.gov/gene/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Biological Process chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Cellular Component chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Molecular Function chemical-pathway|;CHEBI_;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|None|None|5;==;Homo sapiens chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None -chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|9;affects;not in x|6;==;Homo sapiens::5;.startswith('protein'); -chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|9;activity;not in x::9;affects;not in x::9;reaction;not in x|6;==;Homo sapiens::5;.startswith('mRNA'); +chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); +chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|None gene-disease|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|10;>=;1.0|None gene-gene|;;|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None From 4edd87cb76e4d5f097e2f2d47362175f320eac03 Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 12:56:26 -0700 Subject: [PATCH 009/112] fixing identifier columns --- resources/resource_info.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index e3fabdf9..cc2d4c15 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -1,11 +1,11 @@ chemical-disease|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None -chemical-gene|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|http://www.ncbi.nlm.nih.gov/gene/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); +chemical-gene|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|http://www.ncbi.nlm.nih.gov/gene/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Biological Process chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Cellular Component chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Molecular Function chemical-pathway|;CHEBI_;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|None|None|5;==;Homo sapiens -chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None -chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); +chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None +chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|None gene-disease|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|10;>=;1.0|None From 6f499446b4f94654bc63779af8c121b3341d437c Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 13:24:59 -0700 Subject: [PATCH 010/112] fixing columns --- resources/resource_info.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index cc2d4c15..73862ef2 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -4,9 +4,9 @@ chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/ chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Cellular Component chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Molecular Function chemical-pathway|;CHEBI_;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|None|None|5;==;Homo sapiens -chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None +chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); -chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); +chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|None gene-disease|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|10;>=;1.0|None gene-gene|;;|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None From 932a43e47082eb1aceeee325f899576eb01828aa Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 13:31:02 -0700 Subject: [PATCH 011/112] chemical-gene, chemical-protein, chemical-protein --- resources/pheknowlator_source_metadata.xlsx | Bin 17884 -> 18952 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index b27859e52f145902fd24e147f3bcbd213f137546..6dc5bdd3f2dc6ee91b10bebc1c1e445f62ee5b72 100644 GIT binary patch delta 13043 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 delta 11986 zcmcJ#WmH{D(=`eNCpf{~JrLZT-~EHEt=CV19*TxH69u5SPuyDV@Reg{V;|_99@Q@PC!$q_BMJ!bqAW`+j3I{DhW!%$OV?{>@V4# z*<9O;o!c$c61ubd3S#RK@B6aE1SaKGPQgUCg@tvI;|p}e!kQ@9NYT(m(grg9SXG+N z^0x0%VuVKHu?My|>2+pQ(BWKtHxVOmY3y zi+YXP_6IH-ayeMLXk;U?mVuKW9~-leMncZZ}^>zKPAg$%o6hDBfrUxoy62>%CR49+wfUp`^c3!rBJSVSx^L%O zK3tD8Gf_^7gKjbPy?N@QWf2aECaG3#l5F?Gst;hKp7*3~tXA9gI<08?r#0Ur7WNN~ z>i1h;Z6GMOV4yw}NS=NBcnyWW8BbcfxNB6sz`AdJU7?bjuD<1WaRi{W!JTmlvGcYH zu(Mt9dDl1bv%3rH3NBa##rv1K>!J#wk&53C>fgdbK|MS?fqEtX1QrJh9tsf(3W@@X zXmATifsEu+BwRlw1HK+{lEo#Gf3nV&(y=;nRrMKfPx}mQLqfz?Ky>c(Z#s?T*m+^l36vRc^7(~4D6FEYSf6p|8Jp@aKIR_&4 zOS`?F=hf7eK71WakD~QMP@E|rOf^5U_N?F0p9UPUH}a1hY9ISSeTYGo*YELwl{&GZ9u zg)lNZP>)3vrSxsn_N&)-%hYzTtP=zX-aXnOZ$WmyVF*=~_; ziSP$DD8*ead4E`yuFF)Mb}cP`n%$;AI8gmU*uaXEzzz$9P=ZER%Qb|S9i|P{(SGU@ zBIvjC`Y`+ipWUwEtp|=t3QRkLU1`>+qavlf#F0Ug^!WH+P|N15V!Pc$quEC*@zyBi zRY-J4Gysgyo-UNYmAU4_o9XPOF&m7OyFd~=aoRZGLPa-$)N19F`74eAx;v*s7Az$D}9Tv+Hm zI9Mxpl8&toIyR5ev_}=g!v1ntC-9JPNGX+ELs@@Gn{Kg-V)~4?b^DW=h&X>S>EJd! z0>4h$g4_~ngI^`KL8*)f3|jUv)QVaWdHG~1OBded(?U8~kZL2b zAHT$fukN+@I9Zi?@)`aTHt$*2tkPJM>=or5Q!w_xdbQt%oB=AQa#tS9vK(&2MU zEzLofiHnGzower_xbMLCH`fI!2-eNYQF+BI|oh7T${}Er?_Ym0k zf$AQNvx!LD~(+per28TmOw$P124F*E(|<+6o6sqHuBYz1o7#Ib&;xA z*AQQVSSQx09B@tZ*O1crFL#c6?trJqsLc+%N$~w-yCs-c^Er2-n8a2@y3tB1&Oxg= z{xzzN1z?e z!G25MVOt@rT`cRe{ae=3>20^Iw+jb4$!O}`^mcJ)cCNiY43y@#L&HRl?bbo-+Gm%o zLM-AjVo1x|7=}F6Vfrh+=X!-aM*{W^7%N5&_@{SE3m)z1hHUL;TmDa4m#a?>XDW2h ze{gvMd6v@n8};SZC|t#c=+yYawMeZE!R-%zz3Tu}Z69U=0`jR&BQy^Z6qE>Sf++zx zxv&#$f0O5Dh%zwkkqI(?We5G}pCAQ3>)o6-}_k4xY^ zU%L?6O4&IRQ(vN@A2!QK_=+BZz?A@3$-{T`(~k=uQ<+EyuMWhuzK)poW>nZevBYmyG{!)#7=Y!vEa48Y$NBb&1xo`X%m^28z-Anac9%Gm zS$@QjVOB}hPA5Dtl5n!Jv29F9vC73Uup~^6!%&qoTC`V= zb)YTi6Vn%iafh`p%lp*4YN~_dmojKN!wv`Uf}BKWBTm5&+9~v?ID*c)U3luZ!hr19 zRK&B+E|ABk8tkD0c}4fOul+8bwfG}p{mvLHAWw|g2~)PT%AD>OC=(}lI)Z_RSJ`k_B#d(tE;*+$3EE0o*dt`e;0=lx(U zP_rv-hgX^JTh*ld2LJmP)behDB_3b~CDzpR1WvB@Ewbh?`}0JCdqfXj7CIZNXq@v_ z0Xq0FIhw4G?s6IAHr+1_lu|M|&Q zh1nRHey8Ha^7=i;k72M*)okN|07I<&x2L11gl8tDas9}to)@r9aCgo;#uKt=%mi~h^)EE||D zK4|>bUfFdEZ9;R4Es8x1d>`~|GQD7Ycf@EC%JvqF0ASfps@C+B5&NN-@myks@cVPf z6Y}BV*GE~Jt}>A2*D)z zMoAKHWXeW~ssf4B7h&=5)C$?*#+~(MK3cvi*XQepcUMLyvbR?nZ%+ zS}Su6Q*g=C4FsWzhAThLxlMGQbQtcm~`$*D>ip7bzZ-N~)_ zYc~|BW7A-~V$dlAXc6m{`r47_nTch*H5R>UKDm^!nEVIRFKC20T_qatYq-$L9y$SM|eIM9Ld-2nzBAfWuUScu6NaD~|p0_V@mQCJNwOv^Q z%~U8SI2=24>URL0!Y8iWA`Jqx#+f5><8&F^a6uE=(b+p?5w$Ydn{F^Pg4VM#bGe^6 zSWa*c;;UYy%6+G$I*3%f9bLkMr_04RNnk&Sol0x!H$20n4>08faT8=(&1#pad>S`n zlv0J2?^w`xg9p6WzxX$2)SQ)$RLHq#y~gq}Z9wiAegzGTyu&)Ym2~>*Y@W=>YA+6U zF|OPiA{+p(kq3N!ba6XAlg<{`kz-taSk6-z+73UmX z?SAHaD*QsIg3I>!Gv)pA(x$iby2*a}CxU&S6B^zo7(y4@J3?0uz7TiVmB+u-d2lbe z1!o@sR(#>y>Zr&IAA~>*N3Vnki>P)!-b!npqx8AncZ|N>HwN6d-E~h1dq%5Un4C#F z2E4Xe#eDY0a_diA`;rRfpZG`&`u?O3*qWTAaZ!`P(_#iZzk_F(cHL3Sh0 zf5D6DJ(wrS7yq_hWQKscOD|Uc;}`hlSZ6i-3j3($Qjc;r3ML#j=dC6A7aC6Eb7`X3 z6p#|0C45@Gwt(lz)T*F@ATt0Cw)-{`)AgjS)ls#D*bNiM0s@Cd|*z3;0$a7S1ml zX;nx~rN8_-oYj@*$S<^zR)G{Dnmvk1$ox<&@HH6|Rtu@Ze5#JP!I=Ot|HSRdnbVzR zWBxU6olaR_t)l;j+VE}god5J&nOyV@bDgDg*ze*)oX?Og9a6YKi)WN=cB&@p%r#?Zptm5ZLWNHIh&aARVsLoqb*R*ieo_4PnRU*h1q%-+Cbl)jnscXU?hTL%ICKXc+-mk?A&e-v3z zDd6(f?>+U&j`7aM#zn#N2x_69uB>F4iPX+%N~9qN&RGu@XZ5D` zgnZwY3BG)3u#V@^f}YP+4uzQXjz0+Jf6lo^^^_4LakBN9nG<)r-MdPAL@Rv*%E_YsIU2f7A^fu^mg4P7R(9;sb{wBsvEe(dhcEaIpy~D37TQi(tPl+ z6;=`qg0}9+;T^7*TmANg=>6Lw{F*o66qP72TaLCc`KA^euSXhAn=%)Cm(hroF$xho zMXBgyRE%fb9iwG?N^Ag=466*Qs?i+lG_diee!txhl?Xf|2glP8m$buPUS9XHiCpi~ zUHZ4@C#d0uMbV8xY{0I{=S2ASl(QhFYl>5c&)1Re%%>wo4M>Ij77TcM;-24OSItF; z_?VwMjyPAdQ{Xv2xp@{iQf__hu^T58>3@zeS#)*el_yCb?=nC$M&y(_y;is4VXPI0 zT+|qy%u2L1$wpgA(jZFdM1bidX7$B9b& zW7@v?!$Ux`xwsW<&P%8dA8(ElM5ie1qKMvA7OC`~B~k3H#$;Mj7sFk?zg{h&awb@N zdD%%fwF%Xu7VbF9e-Qh0bo-9(*~w0h$NbRit9H%0EcP2us?%Why}9MhkN#ohI}-0Y zoCm% z0R7D|SKRns@_~Lwwv4=;6+O;Dc!tzIQ+Bcuy4tJfzFe$u>NCaSX_|1EabrH9E#5Z5 z8?~Bzmu>%T|xQP#rbm)c4K7B1Y z2dFCd6Q`?eU?M&aMI!j43?jFwgBf=^Zi0-AO2&8Nd4yl+yJ(@X3v6aZ3CJL1V1G;_ z(@!A^D*92h>+1We=||u8>s%Tx-;&*vg?^m-+d$ zN+^gLx__KrAPXAP2Yx0q{*r8z?|#Dn`ApD5auig(FjSQg^@u(ra*kvMqEKfA;pPeU&qJ|9oMA_G#4~4xjDaTyOWrcFn@~gMzX>Wrt4RhkGi&yPs!+ zh0DOrXxOrdNN^as!^8FUUi$5FRu-x7?eW@4&)UvvPb1KIcX^Nn+}*$HG>8blCsH!# zyt{IEkY8Tz@O#jhSycw!p&JPMwfcWCs31GOTD@8g`oWV8`0|ko``xY$7QS0nKbDi_ zJqK<34&*0O%aQs>6?}Kyr3nGKR2Hc(MEfRo-+fr3D)hi=;49nXda=G{tkbPzGY)$d zbjdenXivqTwpN>(Y@I}gJ=8@ULGaRv+I(Nb!TM*&2oqJp&6hwR;e9P%%^sJXp8tpB z3CT}aMzomJ4BAU=+RC%>GwB}G%I3{Q?Hj|(n-k@DS5NABfIEq$wyQm^yJ2NhuM=|F ze(53>7Du$%eRtAJm{cAWh(gwJ2m!*?$1|!0kmUvuGY{MTk4}q*hN`6#PXLV`z&+L z@t4gcYr|B6-Z=@{GqO^8Z|Bv|I6Xfo#kP!AHne=zxCPM&x+$VuvKp|OD&vIMRn3{?B@a70R<1b&BzukQCZ2Xi&nyuX@Ss> z0$+QL%Kemhl4TNc?9jz{ss|55I!KDTBQ!d}6%@QTLpSf#7R6Y@pl~Y5CPer^Im10^ zxy>mV5>A}F)g|VmMh9cF7QmxAay&ZlIo~1$Hx`Z5yu?bW99jS&Jkzp8=Ihu4XY1__ zk_KSh;~C(u)plv0{9G1RmYg8(tvbyyp>zY&V(lAlt|v)_eoNyuG}V49$Fx{7@C@V{ z5f-!is||&Mc9lsOQfegFB33wB$S&(NP zb%1FcjOu&Oq~Q+J`QthgEu98xQkK7>W}4MT%`^zY-9qC1w3LPECx^>p{K=d?wA>&l zb?^rmywo6QcK8POL{L+}6I4(-kn4}X`j2c`nSEfr1rhD|#=O`)+@pAhbHeRcd zvD!XfD=W^%kD9gznopJ*{Bs^t(s;4ta|`g7_a=yUnn&-l|Mt#79GCv+o#o%&M;%4| zAH9SB?Oht;UGC94_HXZx&NJO~!yELZN@+}@3~mrb<7N1}QVK*vO8BX!uV1AB)?8W^ zi?koLEYe`iNkeGJKyAZ?d-{B#Y%Ezrb z-3p4`#oZutUPZVSFvMvYD68q-)ZN*YXvht2@xOX1s!H3>3CE~DMc@@b$r$6VtcMcE z5Ajv>nSZoE6fn-PD*0M3oLHcvJNcVoIU{z2Tc+Z(W^mZJdT9v%9yg9zH``x{z#W2c zyw1Es3AHM@6B%?+BTxO6pOM;PN{?L$3$^m?odSCal>$g|KoEfern`Gq*79!;1&vY) z=ISRDGpvx`KPEfk?2NO1N(k_P2&$(j{C5&`((Y^^yxO@lAd=J}6p^XH(-V{6^#w>Y z^>l~7_(KX)R#~|7s##GKVVFsU8uZ$@fPu)qsD$G6<8lXp=9;Eh)_ zXXgrP+?%g7EY^jHb$Nyli)*>)s<^{s%1&$tHi=6+8y}HMGd?Kd%lHXmLm>H z3|0V+Zg?m2O*D&88fsuSW~h&&C-<&GtlC-wq_G!_|^L-AMFp*?$j*O zYF^V-X@G*{&g#twCVli9u=7-_j4$ zf6>@L+TWlUkPLzJO6QIp3GYq$iQ@A;FPIyz@X!1JU_TI{yMt7=Lji&_ z$yI}NrS=p@>6<+XtAU2ht`<_tYWv^l>|ZE2vuo~ebn!3rJhSWiZ*=u96gR7jknF!6 zHaJQJ_aw}O1MGw)f#Y{pv)hRPf0WgLG+D`R^sk7ov$}L3_K?)pqHFxxF2Y6*xA^D% zVD|hVq$d(lMbsY`kewd?WAkXAV9%ULBt{^|LstI;xVZLLo<#!uL?CW_m>rH|8W zPucJQJ)RlAfFT_y0u@(2EYtyR`}jt=TlgHgf{uHa ziEQVOUGC&LGSm<7R1n5uk2{c>gPve3-c#K`7@NRfLI6dv-%2tBf7c}t<|Z)w_j0wG zYw`Nk8r<|%JmHY8R(xI>Wd6R@Fm&m(Mu@&jI9vwzDP&oSg)^pJU#BmDr)q&+Nc{=3 zyGSW0Qy*1frD+^ahK&^1Vk4JfQ-X>i3qzWs1hOXqD8+6U5aj@5{%iUw&G6q{9rD=K z*|iMwAuxL(5bcoOHTva!S=?Gr_@;1#r@f2-d7t37tCDQ(4Mo2qTly-yaC)qMkFsr@mSqDg+AXmAh zlsO9O;h(RsXY1VV_ZuM#Z7&?=Cj6Iq88t+V4jF%X&3pQ)q(|)#7qpMb_UE=E5rvS0 zSVzFzMEvHR^Q$rt>wNmEg5UDeBOj}trKn5y?fx+pEje942j%ZfF{fz^76ZOF8#fnZ zJA3ac4ertZds<{0r-By9h9OxwehNh?|7spewsn~l#*IPxW8S2vUn%^ZD zG;(ZQs1c?GkY&u`5~p}pFZHn^3BHc+>;AIl+&T@yaRxWk)9P22D3Ld^pQ~W8#gbID zz9pfAevChRFU?)0F?USPRc(u{04qX$Gd!d2vbQ}0?~#Ga{wkSv(f(XDxO8t?;QHzy zN%ezowEr6o<%5t<^3POVm-;mF4r;^D8}sQRhp}t(MCW0E$?6f4HpnTg7Ua+i1M=I` z_PL3Xt%-!Ck(E8Gr=6{7oc!ZmmFPvF2V(7X-C2;_1goU-M+C&38QfzAS&a7f+@kyg zbHgPz2Vt)(wzS2uFt9fC&I{6n!+Ge0pROaNYEmXR&pCtDBCNULHRmYc` znO2F3Vgn`~D}qJvF;C^Ev-1ary{M@2J2taV=u5wmOXqqeQR~{vKuJ}k;5o|ibm{28 zr1-zQ-_5D0sR)tj z1_K3!355u4>R|G!HcFBP8VFL7m=8!03V+fa`CV7WzR@rtiF)pAIo-YXbprxfg3DpD z3v2&t1PufO!!ldfSQ(sLC{Bmq$yVa_#PlEVaA0;8twqbPD(JZ7k$a`ljfp()nFn)OffhDD6chKHNeR-g<7+R>tG@DQZUHr2<^%8y(l1uRhfap*3(2l{8+^d?!p z;0ud%JIwcORa?`aRWYFzeT6MtfO3gpkki&sxagLsGItw3SFrG;MK3K&b<^!UlM8Ix zd#?1M^_Ivae@%iMhKvtH%5+Cx8Fl8@EC#vYqo$U{FEkV1rp!0J5)QuP5D6C6FH%c< zz(}TP&JX^^n}G>HNrjFI%l$-6*p{C8{o?oMmwd?oc#{-CMjkppK~091= Date: Tue, 7 Dec 2021 13:49:55 -0700 Subject: [PATCH 012/112] chemi-gene/protein/rna --- resources/pheknowlator_source_metadata.xlsx | Bin 18952 -> 19261 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 6dc5bdd3f2dc6ee91b10bebc1c1e445f62ee5b72..a99052da199be043d1d6e2d07496e6cb4a6e3cc0 100644 GIT binary patch delta 11563 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 Date: Tue, 7 Dec 2021 14:04:13 -0700 Subject: [PATCH 013/112] chemical-pathway --- resources/pheknowlator_source_metadata.xlsx | Bin 19261 -> 19604 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index a99052da199be043d1d6e2d07496e6cb4a6e3cc0..534a5a0e109c87da44abb6f54ffbba17da6ea091 100644 GIT binary patch delta 12412 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 delta 12112 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 From bf0882714921716a784c3ae52efcd392e3674bfb Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 14:42:08 -0700 Subject: [PATCH 014/112] chemical-go --- resources/pheknowlator_source_metadata.xlsx | Bin 19604 -> 21426 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 534a5a0e109c87da44abb6f54ffbba17da6ea091..033250e6c02661638556bcd5935c45424bbfd7c0 100644 GIT binary patch delta 14311 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 From 882c38b2ae0c9bf45b2577a8f37eb100ea958d11 Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 15:22:16 -0700 Subject: [PATCH 015/112] adding reverse edges --- resources/pheknowlator_source_metadata.xlsx | Bin 21426 -> 24393 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 033250e6c02661638556bcd5935c45424bbfd7c0..a25df3b5239c0b81f7b7d5676a20f76ef1e9dbe2 100644 GIT binary patch delta 17538 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%0D&Ee6D{V5%$z5MY51kg zoEqigaKEcIpfVt`vg2wz=6^GYrs-e<6DWI`i-H@?y9S%dMS$PAk0%wK* zZ4vea)9bY%DL7ZCp^MB`T zn$R_DngGatCrziyZtj2}?hhZ{X?(eF2Lb{wlP?6NZGNY zmX=J`Q9PJ99RyPIr2D{BCB6v{n062I@AHv!q@^rhof|ngc6+F*1tED@4sGZ5H zjt~y%_kYPcSmeZ&_CMcmZwE2gZ7CZ=Mud`&v4tQER8eu9Nj8vTX4+7iHXB!pC1mfy zr1))?;?-0cv`vS(G3)Dyv^6L;)gm^%{v2K4lCbruTMapfzqv#pLq%&U!se(50@Pu+ z+f~#nX(B;%M8%_w;Nt0tJoW2sNVUKq38+2{pdy$@@6=Oao;Qk*o6`HwdPt1Ggh!`! zVZl|3F8c{}3k&%8JUn@o=K7iWeD93PA*SwN*~ZkDXf<>?DQYsqW5Oz8;bN!(=O z(vTT=0-K$8D#Z1PpesLKedn4nqf<(i*|1D~C({r6R6d2dyqtR%8IDL3WfyJ}T9E-UnrZpv>etuL7KIGsEHT<= zdT92-;AvI>!Y^c0n^bP6U;Oxc{r%HuJyAXpYq0f@wbIcE?kmpxcTr!-HZ_Rhpr~nln8DA3lTdc?Wyu z3@HN;g*UqjYGjfuy7Z*$(m5U_B{l^1#>#r)JCoB^{Rqumy1VLx&B>lAwL8sN94TaD zIM`dsex7`R^cG~05Yri~)&Oe=ogLXK*P zknU!`PBX?g*vdL4wjGz$sI8u>&++4Y@!SD|N0Ee$M9AXnRO*S2H^rdY2-=+t8dLR; z$T)=XXom|`QX;xR%W}!3_P2z+`l=s&{1!!EArfv&i4YzX_^YaF$r9f&NE4b@zXq~A zVa?5F59{9KXR56*mD%&hHFq5W1X8$!jD@2e<;gqkWSF`VOi6;gmEnW^rSS~?eFEw& z`n5)3X6Jt+cnsJTC5=3gJeW2YH!4(3??2B@0twNi%`vHK4<^Pu2+Ei2DhCcr~W36>eq$=SrjzcYj_W4gx&F4yQ{pgB^i>Dg2ON?KrGWFj0B zEmhCfoRszYIm+z&eqU-=rZ9dbyeG%=&UCLZ+M`mhX8tHz^5B~aHC7cqCN6{9W$+pU zdnYkXpFOV|F{69_P3;r!G(oB102~E*yfxXa?zPE9ab+4$VzHD>g(?xb6L@+>#WVB$ zC>A0IM}bapzZS#iyMqN`KwX0z(mark&X zZb64n=UMN+aWGo_h!mE7c!b)Ko>sl9TdJT6IFoevSkFUKE;Cht+Kq=-MAZY4W0~SX zw@TvMM}^FmZD@2;?yWte%O0*lPf(W|I7ilxSR7md{RIbVfm52S2%z&>;qdr@3t33N z5p3)!2V(N)X21LS6o$QlB5JAwDU9B~pfp&yTeq9Ov zwm%oLbFhQ1?u>3?um)TXbO^-(syVgQydF6`kYpYG8ur_zSoJ$^h=}w-F~0WMR&`QM ztjoqApILR(nb7_dfWZaOT9iS525GXbBv}^iFy(HI@P3WGrNotAF{*3~hFMesjcw+$ z?gh{)AxF-3dAa+gJNA&k$I>FZ0?SsV`0j_V@Xeje`s-8e_Cn!HP2M%lJ}i*cN2?|i zSKW0jk=BEvT?;?cMm;RtNVWR%c85ld34zFMdB4Yb=PaoP01)(fDEIhA9 zoeai&)6%5jW|Ts+j3p)f!y#Gc4viB30iy?Tj*BBo>Fw~PQrP8l*lX6SDodVDKh%v_ zSV5b%bp1RN;68vy6^I%I=G7llwX-urr|B|IoqHyb(#;dM*H059XqNVs*g}{hQui>0 z6Ev@zI$f|M{Q4f8NduShMa-PArB1%p*VE_y^Kbnt(@z|s2uLu|v(Z^KqbzkT=46Jj z*da-2v&0)<=$Og?Zy#I(4up_BIa)>qF|#reldsYM1UZJh1l6n4TTLT1Z8h0mEfJfF zWVk||YKb&fPiRhoL!ylfff+ub04OE{Is$7M%=skhv)V^hwQ6a+@rou1;dGp43+3UWA`F<7?RSW^YEnofZS9=rkUwtbhZw$~nu1W;4BURYPyv&J)tiXIdn zjNp1JffN2B0`fIWq64$4ax{xYl|%j0>Cb>jS`#Smkhic3pdG>x1U$k=dnOo+)px!L zUOL-BJi-=B*vYBBV!ag$3+#?gAW3!{rRK&0Ha2i@X)GaXLO`5PX+mK~=Z=r^2ZuJ-h#-~vBqg4cdVf*a`H$xsq8af1&jma&S4q(#D$F|h_fbE-!6 zdU%M7^5urKU88OwP;sy!U%!0n+tIP|m_M)E#osc4fY+5hKPxG|_!(o4pPO-)i{uEv z$1C#$?cLCb&}gW7Ib_7YYIY&!5;5(>;MM^bY=C6%a+VnKq!H%pLEPSiRq!8={i8}H zAJUSyHphS6ueBIiJ$T>=wKW4qTTQ}Kdn(WCu4#IM@L^~wY6d;ps0f#W^RjRY%gbe5 z>k`$)1&Bn`ZA>(^SMK(jzk4kHCbUAtQ(P&j6YjDfCGScQfEHEYnr94uc^P01z_6XR zOKbK5WkPI53QyEX6uE_IKbS}L3LhKx+6S1N<)Vv>K|4bufI6cU69%Ye1M|)o?=>VN zUT-nST$iq=nII2gHE6UHAtk0>IC_${DYw8pN~nHYt!xD}$vUm@uvVreki(-8w62HW z1Zd%5qg<9sX~>h^y&k--Dxd ztd24jJpN>?6ipF8p)4C7s-rb(uCG=|25$%&Dj^=+69#pyzxJyCB(V-Cv()i<#aO70 zF`Oo6Clrp9lm05eybrnH9IVoE(g=6oBbky>N#1aZq}^}%fbT*-LanTRAQYQ(@<4=m z?1xXXjFQF#E#Q3j>6F6bZZ&H7W~RrAORzOVEGs|*($w}VX^n{syBbpxEhtstJc*-1 zTpH~|6f*DZ&}f9yZ9$1&y~91L@?Rt%p2FeB=#($^Z=;G?4E5L3ujbM3C1nt0gc(ro zjsMtRg$y-7F@`Z)m{&^*yVgXN52xw0JlL|_#lco5lci$)i>}`Uqtc|F4@nS~Lb^^H z4iMdGEoX>c#TnHf5@|(4Qy?Uepb|aE){Jlirqyrej5kuCQbh|ja?NtHC&a~1NARvqr&vD_QoMS2qKr>?utu$&}m8)gNN_$OWf>g42Q{RyybArqv zwoHX^bggN3QLQdfZ?wL;Bh@Z|)B}V-oyR`ApnGjC^F%stJY#0*$Z9c1Ke6ikf?e^Y zI?kT1vifHxR3(v3WZFZC)6Kx|e72-ms+_mmK+M60*SBD|h$?1Xvi>mp z3oW5%Dw!8nH@qB_L3)fTA1~}z5gzLpOgWoM7E?;^ho2vA1{N_V5D$76E2ZXtX(vcl zyq-bk4=#34@$8o*WrfQ~+s^@HtTgzpLY*%r95iCz#byUX);F`Mnhp{6VMn{^^!jn3I*1? zf^G+HoH1pgHu4hbl`7H`mc0uoYQf^p4|*~1Q}Tk_$R699V_SbVD4qtC1%H)v!BeYM zwG&efGb1^ax1eij35^1>yD*e9R>#lJ{S4~-VbDdWuZ%C|#XRGYYRg%b{as<5FFDjX zyqakbPY+XeN^LpmWcdWSl(5mjW9Xyq2nv*-ml3~9sn|QT_}|$xK@)5(if+lb2REK@ z4!_x396H-FYrdYG{;;SAH21)iQfRxLj`e+oalIKJ9thx2#nN*>*>1@GBHI*Ky?NO% z;T&20{ybpG%g!E|kVN-TLW8d5X2Ha$=S3y7cS7COrQR+3#+Zhu^J+8ZDB=e*5~6CK zFoW&T{QTP*&80mbOl*Ys@_8rG!re&h_phhtXSN=C&zs2mu73k85|&loLB-eepy}yt zixAZ7Hqg;asYQz2T3^Pz#x5TKr|8puPUERW53x~UN}36Rk`u{DFbFiYTT9BHf?iD= z+h?aWaOFlF|s`gQZ zpXB3bUXUlmOF)9i_DywW4Kiv)Ik%paJ`wRTWpbTkOllZIzg!vP;Jn!hTHIRfN^46E zI!SExK8nM+9pSB$nVsO)#fd?xh@GZsIZUj#`For}6FY_V;bT3XHI((i!`>wtc2hmy z4X=T@)z}4V(+s9}i4SM)W0T23?*t*W{Li4-Dh)rNGyXwrmR>x8hCp&LC$Jvqe zh`Uki>$@IkYEXW7CUTN_NhJ)0(AtQTks+~{(}vqi8e1+=K;TmimZi9?1=CrO`eYKzHz?%}+`pc44|!f5 zSHOaS2}^^4Vg2hlx0Q*Lg}IuGleL58pU2%t`iG7y+-R>|VD}_&03Fz-`fP4%8(!j! zHklx<`qZLBsh||A^fCz>%H#RLm%De@4f2tnv$*3N=p%(Ze8Sy_x7SW%_g`MP5PoFA z$?(0mYjW2`mPi}+9Sa2X4-Fpzabb}uu|Eo-@KJ=tKqtg(c(3Rg&|wGG%M){#$PQ+I zRP$29Flp~M>+@&dB=19bVF)aVhVJdj!YC-HYhSTB=An?+f%Nnlnl;(}Ii3lV!S-+) zO;pNs+`SNEs5Nb~`^ia#r6G6FE4`l~<)-dZC0nai1xmN?9O;FGz+-Z?F~ zyiOz6`2Oo>2lgC{p-NsHfab3}+kkYW`mL>l3nYV2x%yV4Ay=e+a^Ih( z71)@l;v>~!X-ohB&R;)VRxfb^x%*8;DHw4m_MQ?wh?1^Kn8}ENQ#c7%P%)Km{JaEt z;A#Rp0nL-__TH6ZsQKm2S^KN$#l?J>VS#sf$z#(2g-PFjE%a80PO4{v_gC4Gf} zhMm#m8YbHegqfWVcP1Jhkg}n_V@xaf2A*ZZOJ5%daMP^@%%Aw#ZkRsaRgz*hYB1wj zMvVx~l%KVW<0;xtLi=M_6ibi`iDonlOtpYzv+-s^AuTm-?{^k9ZhQ}Ul$hePvnTfm z*00>7Pdt6lil8Mj&JEO7iSD&4%QAOl65+Lws%Q> z_i9)3)p7Z(n?J?m0N8YRQyDRj8jhuu6?=qnS2`F<8A2 zFeZ>y?=Lt9^>Wv9FRyOC#aj4&?DYY(s`z18-ZZtje-qOPnJZb2_?nYg$fB+=&fr(= ze08u}2|#ESL7$ndU;=E%YCQs{3i5x-rLRxyyo%x&wVkQpOjDx}&iesHlBjqXc+GsM z1=tJh`rK!=u8F8rMCJ)p%ctj8-QVFSy_SA~Myj?@i9{lv;n0<2z0UtFSV9_2+L1C; zC~F4oUv6<;)}S52td;#0LesEaEd|b16c;Je0bqzv-c|NB0Fyr@6;}hY(W;+|nPO$6 zFvkV$$B0@i0b<;OEln5B#d!T+tzr#Zifkp?@ zw14+(kHLRsOg6hv2`QVqLxeEvg&gg&fL%#(bzEJ{zH{bH1AQ|k+8csRS?X1gu>p2l zEd@Ao*mB2FU7Pqc|McSE_oeEZ(0JdwktUr-Sr6!h@iV{8U*l)Zm9fBj*sU8jK#0A4P$*v`j zfx{T7BOQv!3hvfkqsvX$`7CBG{cp`NI#UkzV-T5^GT>-CCtz$InNQZ(Ysp^P*8Ei^ zMs*ql-13=G(`;9EccTx}jPKM>T&!I?$dysylhun2wIC}O9(ACax%-KnXkGn;oA?2_ z;t51j`26YzUP5iZSJZ4Pon`!m1;|V0%Fhc#KUF`ehtQld&Z@EpZju%#e zMwqEF#fajy++~Ndw7S)Tl|7s+?gVueZoG|JrI~nc;#o#4l+> z$?`jKLTTkU8XO2x-L5E#$>em3k4yrHeH}i3XA>12q++xzd@{`~vb2_Yfo@^S*JYIo zBf67H!Lj>`v0nSbG=D;CZPi!7+77`CtYa?ISO2nKks{+e#_8Icv6{_JNtwvIw7{{k z`UWu>KFyWXvHAp5-O@>Jg_F`7*>Fe8o`5D4~L5uv3~XOryz34+4mEdvw-i%xRw25w^_sbzhS=*kWdGd z;nVAzp`*FhY+OBCe_2*2U(nyI0|a^ncTa+M9h>*qJ~f2dqQtPK{v1*xwAIHHc8o4> zUCHe0KQ^$H0Gmi09rKqO>Ysa1Bf#_sxj)#Oha~HeTwyd6<5h6zHm; z@`*dm&JEUc$uWwc;YcakuqhMru6j*l5mkh3jgLC8P&qO4P=v?j4s@Qw75KtJ2C)5ej^m5c zGNA4$n^JKa4BdLL%`Um5T-W{HjtI7D=<~-+i0qpU_ams~>KO?Ja2ni4zCGVbHeh9U z)epf1$+D8Kl;Nyf$ZEw_^UyC37JPUn=8X2Tx|N~0cVCfvI(!~KHmE&J*zvZ)!Nt&> z4J~t{iuZHtt74X=@SK$ZR6*xBTD{(7XgcdYC^VpK(~gPBd@k8E~h%YZj zmI_&aU3b>1BriStHASoi2A)BU#mJ_6%MHPK2B@WF#@dd29j+n5@ZqbsRhs=2V z78ne-N0t^YIhJ_`DSv?!E*L}!J~~bNJ{KGtbdDn@;SP}Nd{O#r7(V>Of(vfr3yu~C zT_k!TM$gLecRq=$2E!^$Ehvv5e0kn97?w3$X>|t&_OUAg!twS2o&Bw+ zfcx8524O$H*K?l!oY&pm{lmS}ZK79x#f#nj=Y7QK_{k~^S zyr6wySk4w}$C%jUo0ZD~cx9Z0wNoZ3$CBlD-J^CK6jY${uU>Z~F1ecZE{$2I*b#A5 ze^YAR<)m0!nL*wUz#_XI+<1+i>FcIU@;EQ*3Z^%rs(r|7ePle@Iz}a6@q41+Veuo{ zX7Q6eh=NrOZekY7ABdvaEk*=*j#j>2+@2P7-nGIq{|f?m!>DH%ZZ*&pN8#pv$2j3$ zNH@J5-!Pi#?-n}QibK_Z;vF)2ik`(qErPQ)5TI+jGf(*h?3hSm!gVBKd~`S#;zPO6 z5~jGGgQOEXs6@T3K%^V4D}z-v>_5KcS39UYrI&IBcz1ShUTnkOc^;5=F+mlb z54F9gln)X^$67fc2csugWbPCA{#hBy2@U&7$AOB`JcKzSqEg1104p-@P7@bg`G?3Y z?!bZx@jNpG9}64%??!!e9QJ;X-bbxK2%TqF<><;Nurm4Fo85AQU{CE%xSg(n-+7bE z>VRNM?w%$4hHf$qhpMPNUM-L>Hurb-!`NiM!h)R#h=G@+qE8$$1nJW>Io1BW2voGnoOBejo< zSy3zbhU@fU{FXJ8eaDAGFkvq1o*#io+){v;s4)Z5X^B2j^bwN`VE(gyu6ZhkzMxzeZ9L2iV`zw|+J{_~bK)V)7P1B$D47e34g7JB8u3d`E`m)%lICjN;L{0!+A z47lsy$h?o}7$}kdBun9|VOpgLk)NZCKD!!Eouwv_`9^iC%>Z!Ux3I}Intb>{*|Z9@ zytySeMzEsCqtk0rmjn@aP@d6O!$8QFKI%#I_#j596&@1*kP$qq0mz045u*j0XNdr#Zw)vW zv(o4IXwE@qxhky}IAn{#w}6;Gpj8Ve^32)Hpw%vF51dm!7NevTfNl3(rOx%GbnBtV zpAP|>1!_1bccNuf7=#B0TavZLt*D1ndewMW0}_o4K-LU-PC?5bD?f8ZIAq4B@kuOi zwT4mp)!>*C?3|(pdx`DBa3LlnD2GP}uYRVmryrm|zsjSTzty4>`YR_GXn|5~L)QPxM~at4QI^F3EX0WUr9 zXDpPl=q{%LQzk?^t)D^BJ}A%FaG*r8B3e5R49^w+ z(MsMYoiJ0Z0HU5OKIfL;7;y~Yg{ZH4M>7KXaK_m68914r>hdRlMU)b3*_|)+Q~Yoy z!0JKaNA7lX^lpgEb3^COashn298g34%#BSJk$t#62AqJ*dlN~EW{&B!&ZubLl%@T} z9HmnYu4vqBrCQ8iqs$OW{5nuhY0Mtep1Qotd;%CerJ^^kWr|#XWy36bGcYU-2^SuA zGY~dgZgY!CCZn>HV1~3ZpflxegpwlLtW8?Rq=bCh1EA?FEYVANddcCc6j_oufps|; zqYjAc*b51Zag6Jl;X`hCcz5Dxf_4CpE&4*f9Ry4YmZhcO5g)RrHi`&=J#&l`@Xv(_ zk^P-_Wtf#yMQ&A$Aq#Za%)@|(m)XAgtXD9khrzE^I0WhjVth@^Ptve36&o`&C^PRoLm>2T8{(wy9a-EZ+?M4-LWm9J1v0v#h7j!cRN1G+8Yl#nWj(m|(cx!d- z^aJq;pG}RGc;WE3?&ukVqovF+{vc@8MbXb*++3A3(PwM=1c2Ll2s@74Px^4S~9%TlR}UvDc*?3f5LOYIBY9yccrjo$Ha-ki95kzUV&xhMZqP1V3?;po8l zghbO3H0v3eA#b>>@pW3v`)_L3b#T(CMPTh<`2PAK+LMHbGrzqHLOd2z1cv)`og^M^ z!?8$lNg?Aafs;Zw01atszMayR9ln;?M2hJzQhytoCyFmx%jGyrGx3o>drKh1BgTV| zyn!WvATav>2adOr%X#ihAxd}=B!?b>9s%2-rvwt&ePQzCp8l_>@$yb*zgu>eZfr); z_cz{AsQT;sBZ<_AVT|tmEK?Z8T+)G$Czb|x0%r$n2l@+x4!sQ@rSAxyxQ9N1gRX!7;u|l2L45nZ|Jnaa z}`nWtaDrC;H{;RjFfdq8+IqkkKL;8qXB-XMJ38y;lzPy8szzYGYU z`%nA>aVH3#`Ua2sZ}>RK{TqDazu}Kf8x8W4a23D@5n;fgSCHy{=c@YgTDFTF!}~gU z{xz(4nbOSlhowvN<|yPHtqMgRjoh2ZIqkXq5`Sj%beE3uyrzTB)!C1DB)jbu+wski z-ztc8sIwgqMsU72WDb2iX3!|jtf1+r1o>hFD)K;^Pp7WA*hm<1foms#ywNz*fWRrB zC=dYl{cjHZuBaQ#*$x#q+lv2f0nuZ?zou?5knPezr~M~D5lg4Foor8o@$ZrYypy6C zgx%9@+S>v42tN2T(9rr8&9v<|wwfT}~} zu@k^yJ@{>;%Zb9=UPlc->0guGF9`(Yt!*NBdRPsA*YQY9(P=43cmFay`Gb;j` z-f-VQ(ZSO}rqIRH?#Bvq*Gn?Ph2KyZfy6qRt{rrwdWWFkkFvE@t{vqMe4WkzHo3Ck z$<}3Y?BT0WX#`aA6I-|$>&#L7v@5#)&~`lfZ`wose`xQS z{x@wxJP_@Yuh+V;v+n*!fRutv2!fH0^}M5Byj**0`mW6vuG8&2KrZoRWCKU#mibc2 z!pzE=mLedV1SY^<^dBjo`NOciJj0V8l&>S?jlAVq zXYPJgU0L_~2XI7!5dd1b3gQ*P8tFZmT3z~i4cNCJ{{LB^&HR5z@9*$T2>&ZQ!vEp~ z{=Yb_?fsp1fCxy7^t8!6-xTHwCqXg}wD=R>%Y144Gf0Rxu9-|S<0n~Hzx~~e_GDP{ z5Z|hUp0@gdnMZ?1fJQK*;7$8~l&g7d!4o5RwwzMbwTE{w9Ue!T8%R(f`aM@@M)G*%G_CIcd=>H-4zh&1U4n&v4 zVs!ae+<)rqpnvCoP!$~gLv=6Ve^4E!_#aepB>tN!`+j|Z%1Yua=q@2j*(n(IzdL*! zObef1{h{)3i}+hRNXiZyVc);}`j*5Gad~kl@fv?EZ385_4uIO1IPI+%eq+C>^GIAG zg(;193I}P;r5#s==lW@>$GBkat!^IC6D+2;ML@GPIFXrE=mxaN#kpC&*~m$Nb`nSh zevkGRJZ6?(l$5qV{G*Y}U{eP}{ynTJADl94|9_Pa*aeJXR7E z9Mr(PVC_?h`HxQu⋙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 Date: Tue, 7 Dec 2021 15:41:01 -0700 Subject: [PATCH 016/112] updating evidence --- resources/resource_info.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 73862ef2..3cd91215 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -1,13 +1,13 @@ chemical-disease|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None chemical-gene|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|http://www.ncbi.nlm.nih.gov/gene/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); -chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Biological Process -chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Cellular Component -chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|8;<=;1.04e-47|3;==;Molecular Function +chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Biological Process +chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Cellular Component +chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Molecular Function chemical-pathway|;CHEBI_;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|None|None|5;==;Homo sapiens chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); -disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|None +disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|2;!=;NOT|None gene-disease|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|10;>=;1.0|None gene-gene|;;|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None gene-pathway|:;;|entity-entity|RO_0000056|http://www.ncbi.nlm.nih.gov/gene/|https://reactome.org/content/detail/|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); From 9af301f8d9834ea9a86ff4c6c6445b546fefcb59 Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 15:54:44 -0700 Subject: [PATCH 017/112] disease-phenotype --- resources/pheknowlator_source_metadata.xlsx | Bin 24393 -> 25553 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index a25df3b5239c0b81f7b7d5676a20f76ef1e9dbe2..0e8299b492ff7a3ea3b3a72cec968451e62a4341 100644 GIT binary patch delta 18265 zcmZsD1yEhxk~JROf(LhZcXxMpcXzlr1h?Ss?ykWh!QBZK+=Bam$(xxsZ~m{Ks9Wpw z?(V(Xj$OujKxalk>nLC#!o=mklW1UQ0fNPaAV#ejuBy%HclFNS(JBM9Ofpm#Uhcce ziR>sU)mj>rjO+YdI1e^mCLUAmRBWbHZ=zchVy9&F3hgH07Wu`W?$yj6Iu>PSF^YcE z#oS=7o*2;kzn}RI49#U+Qy_q>^3T#xbt32U*IJ4^v24y@xJ%RH5F1hBD{a9@10s70 zRN2`YWamT>jH-J?pogfix@fiJRdSFJ?Di2wr!yq;wlu0vD4NIxAZv(rtPZ2IIPe6k zv?lg`LRZ+x;p_9a=*GJtG_)=+w1D;Cztq75v+=>S#Nsd1>0aif{(;Y%iM>hiwp zpAg$~Plzw2o>JsDCZ(4Y*+hxnrE^X9Boc2765HxEiAFc}gujY$ezcV0kRs{~ zihcG-=)79!5lcRDc1^%5=lqQlB7L=dW#c`I4VkQetaVA*(h-nOpYPtfhfNLRPjwgL zUoLGX(9{lnHDJRnqfEi32OvqO10(sGy$sEZ`{gTF+-IJN-z_GuAWZ#mF7i-XXL1F3 zBpaX-xORF8XHCHqj)p^DVl-UdndWaxjaHxYAu4w-V`t>v%_-OxTJEdp9>{0Gx4@Jb z=H{;qTd?oXwTLPuTP;im?avf`;L{M=uJqJ;OK3)F7WF=;&0QBM#TGR-5t+YUEAT)7zAEgU7mIoa%FC0k$fRgq6v7ekZEn_&IVjtC=e zK)9hz)%^{0oiUMhC4e`TTE9EtS1_&B;$!^W=!QroU&GkB7NMrGw^lhmk%0Y7$}BZ39E^NRf884=5h?V+ut zX%zIvOL$j!*a`lubb34xkowjndu&Dkzo@nnvQDv8#iF0?ml4oPr>vC{!P3`zJRTUk z1wBEsUxUN`FFgmi7VduW=-zv)&}s*15GdkZ<`u}dn;|7LSs4CFRsEbWkqwH< zO1mkH3rXZSbd=p2^U`x}kUdDG#qfBnzQzyauT1`L6H>~I)zQ>;AbWKSFN<%aSs_V2 zI0S%HdcKXX#3O)$%ppsD=@jq=$sHk9siZ#79^zm#jN&LYc6^J%%-#oUb?CA>mrzy) z7hZ}v1yVB3u8c>-C#r|dE>H24nhUR?^nFLzzC$T0uca(kqRnL^Y0+RnO}A z;DL?)VQ6`K%ZZt*oL8OrwyRP8prEC<^%(o`2?XT*9pvLr zk)ED|ZY#1mP!CjOS8OP`a%bYk-d1WVZX%0ISeM69 zDq-H#_&)xX^Q5Ea*pdQ)Lg~T4+4b4l(!t1kH)n5p`$|;l`#L}Gi+PLY zl>WM(x3~Yx*~#21gGkh;*K_q&kJ`<5Cr-YNY<%Cfm)fY6wIz?>xLPO*2Y`9!3f$J) zo{m6ocCY{AVfd5q;=@g>y`Hv{;t=fdjkR9m)ob#UIW1G zZf*?*z>DeG>Mj*?DN3N%AMj}O(5V0J`EcY2;NRfe^6dctyxVxFykib3dcQLXzW`W3 zJNpBEd93tZ816pmP3f29OVA;3lLXDRtCKK1O3FKg%t`7$@DVsnmEJdE9?XSvG-mEs zetDw@%Gkb-?G(ngp*DSm(=OYaYIso)?eEYw0o%0%!}o-Pj0lyMFp2 zMc2$)76UDRCaz@jpg8ay1D6Hg)aKa!2`c2vZ0x+

9J9yJ;^=;p85@!9zc6Npe`z!x%H`wPCw}z9g< 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 From 4b337e706908e015b40a53f95d6cd25590eeff92 Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 16:18:33 -0700 Subject: [PATCH 018/112] updating criteria --- resources/resource_info.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 3cd91215..119c3aa6 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -8,10 +8,10 @@ chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/ob chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|2;!=;NOT|None -gene-disease|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|10;>=;1.0|None +gene-disease|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|6;!=;group|None gene-gene|;;|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None gene-pathway|:;;|entity-entity|RO_0000056|http://www.ncbi.nlm.nih.gov/gene/|https://reactome.org/content/detail/|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); -gene-phenotype|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|10;>=;1.0|None +gene-phenotype|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|6;!=;group|None gene-protein|;;|entity-class|RO_0002205|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding gene-rna|;;|entity-entity|RO_0002511|http://www.ncbi.nlm.nih.gov/gene/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;1|None|None|None gobp-pathway|:;GO_;|class-entity|RO_0009501|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME'); From 01b0626ad971e5e7b1e78632571208a3b9375a84 Mon Sep 17 00:00:00 2001 From: callahantiff Date: Tue, 7 Dec 2021 16:32:11 -0700 Subject: [PATCH 019/112] gene-disease/phenotype --- resources/pheknowlator_source_metadata.xlsx | Bin 25553 -> 28107 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 0e8299b492ff7a3ea3b3a72cec968451e62a4341..d7594d2dcebd5e71e6eadbe96206ce169dd201ee 100644 GIT binary patch delta 22239 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

ww1E^j0U$7$u_uWLh?Pu}$O?>* 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 EAE`a?y8r+H delta 19672 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 Ji3g_y^dI)9n4tgw From 194ec565df7da6a1098013f1123e460a5f82e6f5 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 7 Dec 2021 16:32:21 -0700 Subject: [PATCH 020/112] adding gene prefix --- resources/resource_info.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 119c3aa6..5f326dfc 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -1,5 +1,5 @@ chemical-disease|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None -chemical-gene|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|http://www.ncbi.nlm.nih.gov/gene/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); +chemical-gene|;MESH_;NCBIGene_|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|http://www.ncbi.nlm.nih.gov/gene/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Biological Process chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Cellular Component chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Molecular Function @@ -8,12 +8,12 @@ chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/ob chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|2;!=;NOT|None -gene-disease|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|6;!=;group|None -gene-gene|;;|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None -gene-pathway|:;;|entity-entity|RO_0000056|http://www.ncbi.nlm.nih.gov/gene/|https://reactome.org/content/detail/|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); -gene-phenotype|;;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|6;!=;group|None -gene-protein|;;|entity-class|RO_0002205|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding -gene-rna|;;|entity-entity|RO_0002511|http://www.ncbi.nlm.nih.gov/gene/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;1|None|None|None +gene-disease|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|6;!=;group|None +gene-gene|;NCBIGene;NCBIGene|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None +gene-pathway|:;NCBIGene;|entity-entity|RO_0000056|http://www.ncbi.nlm.nih.gov/gene/|https://reactome.org/content/detail/|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); +gene-phenotype|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|6;!=;group|None +gene-protein|;NCBIGene;|entity-class|RO_0002205|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding +gene-rna|;NCBIGene;|entity-entity|RO_0002511|http://www.ncbi.nlm.nih.gov/gene/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;1|None|None|None gobp-pathway|:;GO_;|class-entity|RO_0009501|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME'); pathway-gocc|:;;GO_|entity-class|RO_0002180|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME'); pathway-gomf|:;;GO_|entity-class|RO_0000085|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME'); @@ -30,5 +30,5 @@ rna-anatomy|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/T rna-cell|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line rna-protein|;;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding variant-disease|:;rs;|entity-class|RO_0003302|https://www.ncbi.nlm.nih.gov/snp/|http://purl.obolibrary.org/obo/|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc -variant-gene|;rs;|entity-entity|RO_0002566|https://www.ncbi.nlm.nih.gov/snp/|http://www.ncbi.nlm.nih.gov/gene/|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc +variant-gene|;rs;NCBIGene|entity-entity|RO_0002566|https://www.ncbi.nlm.nih.gov/snp/|http://www.ncbi.nlm.nih.gov/gene/|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc variant-phenotype|:;rs;|entity-class|RO_0003302|https://www.ncbi.nlm.nih.gov/snp/|http://purl.obolibrary.org/obo/|t|9;12|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc \ No newline at end of file From bb04d89d5737b8a1070fb5ecac6f2cb48b8e6607 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 7 Dec 2021 16:48:40 -0700 Subject: [PATCH 021/112] gene-gene --- resources/pheknowlator_source_metadata.xlsx | Bin 28107 -> 28461 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index d7594d2dcebd5e71e6eadbe96206ce169dd201ee..268a84a1caf8ca85a54d91e963861e8237457d64 100644 GIT binary patch delta 13121 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 Yy`Uf<F#jJwoPF{yXKFZXr~lmjUxR4++5i9m delta 12782 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 From df519ac8086a299e3ef3d90e4ae41c5e3b7d7bcc Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 7 Dec 2021 17:10:20 -0700 Subject: [PATCH 022/112] gene-pathway --- resources/pheknowlator_source_metadata.xlsx | Bin 28461 -> 28879 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 268a84a1caf8ca85a54d91e963861e8237457d64..499922d52d781623c245abb8846b9784c99406bf 100644 GIT binary patch delta 22432 zcmZs?Wmp|e&@GAw_u%gC?(S~Eg1bX-8{FMpg1fuBY&1x4m*5Z}I9%TEoO{mwasSNo z>^-ZetE*P8u02(|W($0F7rdSt4sK&J+@ly03~V8p1&#{%DxxS&8M0~fOLfU&ZEO-8 zqZ+~@)>=*TcKcxs&YEARrZG2GH~0dyo8vRfd2HCKXfL2X&%ULk=xdP;WB<dT-d&Kh zkC~mGBi10Pm4wsrWYM><=jm|oVS9#KU!aBuU;`_-D6^n6$0lDq{%S7+jPkDyDosd= zQz(q!-RA-Y#eeAK%+<-qS|Jxvm$Udov~z+6KuL!jhfi~JT9+bFHIY!yej}a!61IQU zcksya!e%DD7Qlz2mK1ueR2HoMKH6-$h-z_;Z<%bbz&|4F5U;cF>}MOD{n11ML0@^% z&Vu1x$Pk~5aSc9-!?%v{@*qu!2MMuj4nfkR`6dCHPW|v({q=7kE1c?*9(3`)LXk(r z?Yu`LUK2LneNDBZotOC58YMOh<+9Aj(8<jiSr{3Ak>2V_!kBQik-BjF^CFp$-Fwlb zMCI8QUErjMc1c-a$;Q4V8pVKg)#lM<ExS{9hI(oZ?{h};GzkbCHh%TNP|<K;Jt8y@ z%KQZ~s-N-6=uJ`Y7bUiRP~$1+<$v`a6xYg1>ZZVN)jVRok;<@wjdgYJbxUz;Mq!!V zgV`fI+}VP-5y(0MV*dG}@?OmIODFIDdqT;h>X5?>Qn*+=zXWFRBG7OCtmD%nw)e?l zt90@IMNEb2!+eqIQ!8;HUe_OfyjhCgp!o}stxukN8JVwiH4l2B>(I6ik^B|Qkz7SV zB@f~`HcMFAqJa0d@!p9js<CY(^r$C9srD?ovAF`$I^Q{Be1ubs+z7VVJ5RRHi%UoU z1-3wM)4(~vRvL_UVE5Y<aT9BkR#luRi5=Jfqc%T-$KiJ<CnE1Kn{}1d0xPEAi8=-r zeUWl?xU#>)XrY$0ek~><SkuANNsPalQr?h7r^+JsmR2Di645Y>)oMd#Tz2Zj_m;KX z;E<>`AgzV3hU!Ie@VX(znHdtVF9HeoLgZwM>s3TcAvIh`&;>ah{!D*{kFy|w_W5@> z947$E>*_rWO*9S7JJV)ob~}1E%bNi(3^ld<T=qWpca~zm#&5HYG?IxiK~LoGAb84y zLhg(tYly(NAIva>73{ESym4n3P=ZO5DXtlWr}crLh1HGUTpNj+e?iJbKBT{t)ycm6 z(Q?E4<5fLs*XOOx;DPXx<^N~xkxSESTlmc}(4(NYlp4nJff{C}_d9c)a?E%3sq}c1 zOs-$Tm7AL4v1{U1)w?6xU|?V$ACO=w@{mxNU@%~?pPxy=Tn8}!`apq!VG$)iAyNQ= zBuScqWHrqmd=-Kx28w$DEpf2~3rIelAAVy>5QU74CC$($o&hTjmi~Fl?Ch^<M~m%a zKS^>VOMjSW!&ty6!(~e!ZF?*zZF9g!YDdvn^057GXXzX6``}_Y$l6<n9{mM|UL7$N zngR+m|2hG2T;9GXKeeMcDj7$CII;oIr9l)mt7N;_bhHv`^O8+f(;)smKqccv-~I+t z8oZ@UGLxc7v{eLg{589Fn`swehgXpw6OUBo3v`NA<50LeCl}(Cg<|%kstc$cX^NqE zWZjs9XH2o91wY)U;q5i=;<scTNlX!t-|_jmXgN9*!>)q^XUK}12@a-7hT{uZzJAHB z{8q5*ttr6ZkM|HU7-Oq@!?K%#ARcro;u4o-Of$9=#ro5XDk5u2Md?px*78ep>zNxb z_-d7A+MsG-WH@`q%5@|0%`bL6gWo|U&lF{ZKW9;W!QPReMtrd{)gM=`SHDgKYE0xc z_2vFWQ1h!nZFWLX!7X$(73pmRzGg&(?^vTFaD@h{3UAk>8_^;LiIq6_T!t?F2waXk zBknESNhyXO@!e=uw)blCj;qH^=YjTl;Mm2|GLH=3SsRi%dwpBwDJ6|`(SE5s3pEyY z3MTop+EcrYuoP(!ERv6X$FolsTCf!YfnuhuI3ga!4yOT~$-G#1AU_fe$ioFP6O}?u z^NMSDIX4ycU?hPLzm2CLM64^Wc$Pzq{5*1+^Ny7i=e6P$aX=+*?&aGU$-TI3X<&`v zj_zoste=8X_pY-0lW%95M@O5QIiU5#k#0DUx{LKwP39I>NMBg1R2wNJvb939S<h^9 zOVu@&ZJlK$d=ff9xe&q-aB#=B>L|8Nb=MTDks`lHTfgC?{E@bABP$RhpobI-jeS6E zrwPXw_<(cC>?EH|VLkhbYULF!KP@6#H1!X3LykuOAxvrz(7W`4fUZS*x^eI}@Tkn- zcRdnZ0_eW@hst^|Z%7E?e9$UD5j4OBZiUJNW5dSR`mn;m34RQ~pfudUO3qC!*|UO* z&+8t9%Nehzz*FeqFJ`dxyj-R|cqb+)m%z3(4|t+uPi!ETHXrOG@@kJi@F0|-JNg~5 zmT{+G_IvevTiWbf*+<@AVXppwp8V8_Ki(J`8M@}xxn#?w1m@*&QfBWJS%%GS5vx%u zH2;KD`ysNdJM_?jVCMyiiZyn(brOP?o=VdBI|(AJlQzkH#j;%s%(#c;F~(^*KS;!i zHOo$kHeURYEKEx)$-~Vfb2EqhkA7O6!=0V`%D!>I^el<bZ~7{dnlNZERhpl*L}GFL zV^c{IpRT6Mv6UQ39m*Wzp9$-t-<1BuQxR_C$5SO+TXg~d9{aQ~u8<f7Hh+Z3W$q)v z!^aA#61TztUAfUy%LPGo=CJkaE8^Vsb$(-)3SF%WHxP?gcfqyWna^?jaNYB<!uNVB zZ{&J@t%p3C3o3yRUy=?o(3k&I&a3!fr9cf?A{%pN)wd#TK4Sm>$x)LZTEG{0q5NaA zc``7tj_u?xxO9L-FLh)xs*prEO-zj%r{<YZZR~!4B!Ois?VR<0vLvS0fKU#Vv(iA^ zcf~uV;DG$|E-&Mkz83DFe}sURYUZs?ZXxk@dJpN}W>QThIh_VX!b~adg?M5rpNdxR zp^TdVl@mn!n2Iscf|D?~pME+`@LQN_a3)6O%Yn6D$$^e_ib)dqAr91#Z{?&hOO%K@ zm^qE1#ry+w3nG~Gk>GFML$;gqP(4u85o?#HM5#YvhNIFq2naE~g5s{0Q#H&qgRUTz zVDFLPh0uKWFLAg5_oN*SSw@6?NM0+B{B>gJqJTM!lZ_8aoE)eqL1(>wY>hi1*>=E0 zlDYmd7pT93YC2KdC!A%!qwdZ{u?Y5@AG+l^4K1R$M6#6M&?`1(Gb|l4=4*e8E=|r6 z53QkBZXgq97kH2P!U|uC@mo}wcm3?!>{e6CULv|4Lrh_;@_t<_G{}9q^+Im4X4C(R zix^J@)z{m&cuq`WEz;TX=e(~LGjc1OL#)L?D!@?w3M9kSoE#o`==a^)u)qqj2}><r z`)ad#EDgQW^%11v0sp}h*wzpCtqrgkXll#4eExKKk5r<RHnOZ~^QY)z3&@}W+Zp&q z`f2WtkHJT8Q~Q;a^*qAkG3W(MCWMT8@~Xs1^o;8>I0t6IJ3ij_cE?0=07u$5rl_~Y z6KLjzZgDLe@bs{gx4}su8w*%r-Lu6lSYah;jj?iKB)7&z<tHozD&Vw_p+xMs$5xaV zC={4y#Mj73+#{-f@LJ)b>Ph{2xY1<WQSPF(#zWaI-V-)&@jK?*BmN%}{?DcO`*%lu z?xZ*Rusn+ZQHFmW_^p95j2?(~0Q%L+%7_pyUI!sUwv3MKKZ*~3uL&|8&9ZSH=38_q zSItB}iN@1<PaS6Bz8odx@UybB-^|39A8+ToOPTyW{~llNH;X>{u-BU`4f70g8J8_* z9eRa?Hb4Fio%~BL96@;7)%wlNEcE8ZBe<1w>AU{q5#98g*%1bR8%4?>7r2<od-^B6 z{d{r%1UlX}==1d#N5`gVNI*}gY0#BTgp9H)8U8AFUObGSqRmI^B9Z8q=6PuhT46-x zZ^H|;nY#(_Zirb@GnOP%5R1KLle5bR37f1he5d#(c5pKAu`1N}{y0{bf7RRd`cwwG z5`Mifxrw*5B%Xc0Id{2v0MciLQGjI-N_c$udicWh!cyD1a#>e)a?tNT-`=3(REIyf zA?xX3VSUpHACdXMw~wt)RplXPXa;umt1{E=D2l!fT0t?t<}v?9DO0wbB~r>a%>B-2 zsQ*-Q=Xr(=$_#SPEcotXwY}vhB#2nv+t&fi&EfTli{nc|HJfL`0akCX3m|{jQ)E7L zT^nl=d`g~XeCQfuam5FE(jX)0fVX_AnOP6RkNf?Th4rDLtSl*MygnMTYkE_(M<n1| zkNxvoU5EVV=)RS~`FVOmN_X#y<kcntZgugDgd(RyUJrrI+^vtN^}b6$vmegK*uw*_ zorWxcemxd!=3@m|#c-TU>{ms|_C(+;mdBhg5~4_q$uZXQ%HXDH)W|!>k>tSvHRi*z zbPwesjTVp(6ks7sIcpDy8vcY0NR(j1DR0tXI*29x>1&;$jhL+d>e8oW@$px5?88O) zcjK{>{JlK$&xCE*=ooko6|p*+p)mJ6v0>M|uI8aDDt=|4$gpSbCu}^C&CHMO!0Q4G z)R`If*5(}vxC?<~i98*#k|Ti!=GqO^NUy7t=7$YxyNBeSvKw)ZR#gcjy_>wtZpe)S ziusaKtnRRge+6k@FT_e>E-*Lh<<^B3PvwSW*dWHDu#o_xLn-N@go0wH7*Zb$X-sfs zxwQ0=)T1;YjM=<C_C?Kl&b^d^9IV<?{kOPiP!a?S%prk@Q+Y9!h!*f7o;G2-Ij3ut zZ=+)XP9H6PP~NVIlty&4L(^|?Ec^k@38&p`%W2l%W|0XYe;%U;b7iBm{C+7TMt@yD z#Fuqp5}cHeM@FG}GYt*bGN>gEH7=kAS=Lj{p?(WsIvjo<LACPywmVBf5$;xXK)+$r zNZ#sCYWKtWwRc;Uh(Cnk_5%~s-Eqa0;25E>>YF7aQ?}%!8K%4aF{)VmAA&4~B^<DN z=p?kcB>{UBL!8np)G?<&Ch9+Ce!_a{4#9islCr~F#Mb<BM1>5a#py!lS^mT=!A8sN z;y?n=RB<#xd0Z1Iyfn?FvjcRtB?E2XF}d(ywoE1A4Lm;?e91<sgc$CcX@Z$3CorLq zKs(l|Fn{QgvVzt2>uSTzpeQ-3O#U#6fD1>n!OA7k78GDT>kKs>-EsV3Y+sc``3ftS zHCfx7Pg$X*VH?ped(G1S&2kW)02(1PEB`x?)7P$x=p6mfB!6Lvef(7a==*Hlz{<tL z6vw7cb2TVq5kovKZL;I1M%6PA7o<Q)hC`P1&mvI(w<M`&4EitYz^tw*)So@&>_IE+ z?;VJI7sqp?i2(=DQC7nEQbq4zYqN5hsw}qt%ct;85~mGSkE``UM{H{a6mYnloWF$t zHQ4K>RfWu8G23T?>SrC>Vlbm~SNNjucXMe4#b7bJUdi)Gy2<kvnL!1{IxjjPHWpp< ztYNG0oO3&Pv=mGR)<s7)T{YXGq;pK|IW|vSP@}t#G`g5n*PmSoQQDkz?7UVry?aS? zn-cv<b(X}QwIt+K1#n5LRoP`l_Z)n{E9=SCyS>+|v!`!?SBn5%g#j;Wd|V?5HJZo< zHN^~%jI%{#IAp+JgzO6l@%(L2iPK>9+0#7Y!neCI$kROAcw5WpLnxqbA!7N_s88M+ z#hpu~71k@fAp9Fc#J3PmbT$F(ii%JnYgRO;ZkjzXUs6+_1eW@Ls*RjR{jBZ)rG;~1 zBF{gSL>B%<hEf6<1Zm7w@C4aXJ7_Um%+`Ej|Bl`+zt@Wk(K>(AfB?M|w06zQdIq0I zd6lI0l2R?X<pgl}baOVRH1i8bq|6o}5h`5d$xfHk=5tbXQQ1JTji0GmiZ;H3C#Lrd z>LQ|`9eX6u+AU#+xz|H!I4T3cPHJ@WJGFOPLak<H85P_y+Z>)INGD^<J@bl#aipZQ z&4bg?Hdd>SI|=Q^eq@~~T*Z?~6jqKcp{SH9LI$;eUDx97a}V2Wxt}=lGlt{L`K)v5 zd(b`PE{2vNc3s%l+<-sBbd`Iqo1=8W%E9{`fMp#eVSPYaQyWJFw50-Ut?RI_|KV!E z#vY4?8EQS(ufy{MqiKr<;t+yz|H(#^LTqt?Qffr%6IE9#x0dlpcCcR1+n2iSQ9^ap zx@VoE6&2m(Z4~j+&EcKG%&%K%g15LPom48fTEhbvA|aeqOw^OtlXUS@%*q0S(&pEH z#w^rcWpq81mUL-y0R8zZ$kggLc>l6r!nbSpInSN&*Mrx(t=gxo!}r3tpQX6qTG)8Q zV7-mU#bp@R5spEs^3ME~yP;$1WYVYFW<P8Uv>QVEV7Mg@7(h)NYVfB}tEi$^b{XJT z3}%&EtA=H|^KO+$p2xoVHWf@7v0j#@zV-Z4xHsP_JdacN05agD4n1aq1B0`rw3`$H zJIz!~OnV-{v>tUAHtvkk*Wv2?^{Dm@Vms<LGDd*cMPC>*D=ODnu%FkHh_qxFtmiF5 z@`GUoC%olScJLv23+x?TuTKfiyrFn}2D&se7bp9WF(SONB36?^;B0-hU#BsyFW-by zy^WAxiPelD07(z?_$nk_&m5@zC#HcEatukQG~g@$J^*ulA7*}G7{Af@diOItxZ1>y ziCK7vmrJ%qrjQguNDzdn0=3QR_e^KltQ#4v<(BUdi{;K8oUGydTuCj7o<h;~A44+E zz_#r(!MRxn)%GkUs#+d4#a&M_@3meom-uo`>z0A0nTG$NNK?hrC6Pswn1#5a;$5PD zK)e*1(Q+g8dPT?!5xLOXpHKB*Yp#0wL&Sz@5uP;yL#T>IW9?i%j}3{Pq+3+K<k!&Z zO94{%ysM<PMNh$$OVr}<;;7)HHBF=CFa2CI(`3PHEZhIwhyJ`z!)oIA*GkP62>uif zl{vO&trTY`yWFnd0^Xa+Gq7@W(%q^)|H%eL7$VwrjSf{XS&PDU=S+Il@}7(4W}9x& zQWyG!!US5K4?3BsER}?sI)cQMU4UcDl+`w!xlOm{#VdEG`0%mcnbOO0i+r!!g~gMW zt#5$;Yc9lFl;ih@1CyX6v3x*A8o>{9zLyjCNDIu@c+?%e>t;5#`g1d^r1m%L=?xKk zUskxOsPUi^Tkl4Y?dT<&|2wRm!waoB)oU_O3xnJ+F@$J?zy1Ee@~M|j<md~NXw)rQ zd>o(@!NvOOo*HA^ZpH~G6&pm4O>yqegEh2~tcJi@%{J(NtsC&v8*2RLJH`Mx(@K40 zc$|@c9I;e$h>&D64<-)PV-G)rKtrbA)??qB*plZ-4#H!s<_?C`$T;r|hlHZU9amMy zkSK=xV~<1!j^CS5;jIUd+X@bQY^`4otRLa)oOy9KR_DY08M}dVP`zyx+%Qq3#ZHU+ zSxWUR1J=L(6Bc06Sxvs+`iIe7Gyiob!nJtR>~qPjZ6*5fWAh|XQ4?XcW_Y+gjP+=r z6xZ8a$Ag60_&J5?&~;MCA}U+!Z)4-4Ck|scAwz6^Ah_6wcxARzMz~`?G4mgUl;l$Z zgYpB1I#??>JM7aw&&E%ENHc$%TuRJNQ!cxk3jh?AI9NP<YLf>jmL_9#$c|5Kev=}6 z)t0y{Ji-auy8Z<zkkUMX-O9B$sO1g7UrOulFIFX*d6Es~e#^PBh)6zmF#QZaaYY53 zGvY+(c!#cbxLuhD!{SteMe>!Q{qS4WLtnmwgYt7!6Q@Q1i#r`DgKADryWp#_IH@2g z0Z>(E7q=MSiNjLF27OC2UztzBGL*Ugr;M3YuhIK;?T#g)px8ny<`$ZP4_nH{#O7q! zIE|JafpI@Cj7{l^S=vEYDV7Mnft5DonHQU4DXNbFZDS!oT&CqTkLwST51xQw(*%?$ zIjoeQGDh);KW7NcNUpV?@);>kHjdaW3PAa3d4njClX5UaaI@W&btbbNlwtX~^(c># zvUt+YjIEC@iu?+Rd_k#u3KvlZabs?yC$$+#%+{uyY63Z2WM)fcLYbm6G8s(;kG&Xj z(Bhgip2S-i)}^%;GK2*JJENi78Qe}As{s~DFy9-=q-2s!jzChx$_e(QB4J}LAfQ)# zxR%#?pC913&<o>OWA`J=g#H(%@Z&c#<#^?>{Ez*+i(*6UX8)wHEVIXk98-WJ(jP^A zMeSFhpGs<$u8K4xTnln!Q!YfyxYJ7af^g))A?@w!#q;mFXHFGcPq6GnlX8L^2cLFH zm87!F&!ZS`OUeb|%|^6UT_rmKP}?d0z9o!!wU{T=^TLcEME(`x8aFd^O*~GJ!dSlS z&G(n8I)YFz1;gDs<b<m%<3ZZ8yoKE_zjpt}!@Ilv`-SQB58r2XzhJ7^)f?D}h*z-@ zYdfDuQ5CghlqRs}cT(#K%BtCoB<rxedVyXua&O|x{91r>jy(+S2lJ!?FkxfwV<n-Y z_Q$vrY>wT;?=5ScG}Gb_9BBW~cm3_xD`u1j!z-bsqkL8k5td1rd{twUfs*ONM^RZ( znc?CV-=c_|fUHQ$1w=7pFXWIGSu4b^Br$C0gOk+BdckUl`Hx(}8lUH`m<etTfw7ux z$xx$$Gf9~F^bI2j;f<6QfP>Rs$?T4i^0pdGvx4wITwI()r)r^Mm{1CYX58oRqk(KV zVfCn62w5jmLQR37-mw;Qt9BMuV}!7YG!j7M5UteQH~!EWaagf}$wP4@v%mHDs(15U zlAkCqL9t)}PD(2}+(Xcgf<{(KN|CXgF)=hQyh$Z5P_3r1t-&J^*wVSFUuxT$m9daR zxA;9o_9==;O%Q7pNoinMd%Fx8Di~>Fs<vZ8%OqgED@6fy=qOj-e?I+nJ%ZZJdxD4- z5kA4;v&!Apyl2dHl66zhVKFvDK|WVCJk&+R^_d`gC(?1uvrAy{5m7A=2o`b)J!RcE zac#HPNU%ZlunA)wF8md=Gm&QH1@Wt^WAv0$rq?R1m?bmPV*^K{L!z>Z{W!tgq#P1= z!5LrJrl|{5vLTHriDtexQyHIYh-pWLdq|m>au6YZx>(>j{6Xw<KsBvo8g0{Def7RE zh;4X?g2Jd`WT-1|N?b*)e#cqp;6}Cv3BimAkuf5i5k&tro2V%v?$bv(Bs5-#P|o^i z0z$HIq~G5GveCa6x4U*_Q&@%nIk5}-+lz4#?w+yd58H1xCK0o}**Ep~$wrRR!5BKf zZ3H%kq}*K@BPokMceLB0O+JbxKeSDu*6R8rf}Vwd-5pZBBrfuLt&33il`)zq?bPbs zI#YQ?Xci^JjT6>nxonH`;@V9;CoB^21neW-%{9;)t^`F1ouBBea+ckEP#m1}A1Rcz ziNfn}QS?l~?o7fVdgKH?pN1hSTWD!U#Nn;wA~rT;mbFPd!N|xpkY>kDls0O|CHm>< z|BIve#D&4;pd?$z<*DH3`xM<cUH(LyN{qR~P4(nyZ|=BpQc*)4#pwn*UU11!KXJ%~ z|HtVhF}ZTE^f%XA<7zSgMMu<1-(5`)L|Fls;U<t{K8b9ly?j$|{^=xi^!mlU-5ya4 zseA?$Iruc3soQ+S8<vU|V_A222CGj5s4-44UPoqZ=;ePg+XzEro28ee0t>uFV#k*U z0r;v71^yX1pO1lHe&W_PQj##sn}1L63kLeeHZa4>0RtVQ^RlJ76?P3*h2)~LYHT(( zCiePfL6CcPWk!sPblLa-)GT~N%`UiI1G)3021107)o`8_6u<fB`DY^~1qNv{ZX^TR zFOP*9#`uN4_#eAbU+8}dawZ0)<+ua$YA;Bl>M+QW(HePPa?S+97TR{m;<E!36e`tF zqnF*`-y|4g_dI`+olNX2RZC?suws@S(5*l?gpD1sph}YKsKO;|dZF}1=;Dps_)7k= zCC0<14g1L=B!n$JWcGt5!tC7r*MRe#TN6DW3`#{ICT-Yw5awn?PelpJfJ-7^q5{AF zqo6tjbfkhhxpGkqX$IWlc#O|S@Y-0@nC8q_5-jJyZ_+Zj&E-_z!e@pNVR<&kh;QUn zEAz?x^H}d9fZZh6lOzhJd;*cW$8E+ACO4wnl^Xj)epV#NovvH-af6TxJ`IeH(A~{R z3!hWDV8wYbiIrEJ(0Ebd^#I0CtK{}uSk`Qjmjc3Lts;tc!R0@$Iwto!9*ckC%BF*J z$~d4WwH4TC?#=kL#~|qLGAE+Q{m{eshjY}1k`YbGMPEerrbz!n3>5r|DxW6Z%fs$M z=f<UHZQ;KvBvX|yvfR*yxH2{uD^Re;Szvj?^&WiW@xB=6i|=!GJOeU9pkkI*I>WQu ze~?k1FY7qiDv8m93Q=;RDPhRnaAM<!c+m9_Y*QyE@9?3cT2?yi#yu+kPOB=|Gh)h0 zF_}lyG?fJX<PiEgqKjjS^fiqG4i3|{!Ob%A3a95KxxSIKv~yb#GximhK%~nqvQUOl zDK~n*p@malg3X=YPaZf#<$LaXTKZ-B$W&O|N|mBT1qCsvA)j***HMv*VBA|GqLZ64 z>V!5)Ls3|*`4w5S>1)b<gIgsklZ83riI;0ggPWS&=9HUG#I)Q{yHFC7H$%k0N>(+3 zr%m{;+B$bnIwGc>$VKt~qCUmA%?elyOWnrd9npOpy)sqj^Hm^QsnC150TJBftaooy zV^6pDch_>N6G3E)(uV>rPXUt}VqPM@+Krn|-HGX$ijwOm(*t!ru6SGB_aax_?d;z; z^qZGsD|PJ%<xFO`KTO_(fY$T!c6a=L<<#-kcjiYXkpoPa^vIdQ47Q`o%cy9*D@!T2 zHrS`ev(oVq5Wq1=L#Hb?PbPlY3UQ`-KLbW>dJcc03JPL;4nMM{rhzObgK>4q<V=Oo zM<`|(2P#I2O+p>Ba)gnZba+`OYJi8*e9*{lY<c;=&ETaLThoOjS4qI6!tob9;Ag7H zzzQB4O&lM^<5;@O%Z9FQ6$>Uhx_l>=p2rfAN6Din5LrW=f~V|Fe!RS@mKpu)ktgbZ zqLUF%!Ev$CS>@_^mvvh2CU$dOOkEff@dY7*euP(L+2oAYZCJf+VWy@=ZEwGUb7>dL zVXb^Rw&6YmV(UwK=t?!_YDzuc&BhvATmz@9B7fUuCF*4i`fi0H!I`+7V4*-;ICY6Q zjsTG1Kd`dKRvWxgRL|7Ac%A?Dkh5E6vkh_8;xEd9mtJjBaw%^}Me2OsxNPWzsg6iI zAXxe3cXTRBt@z<QX?qxUY0n{nKC{be4Dr_6>rH;yY+3liW~~3iz_X6Z8!sGOYg=6? z$M=?z?{rKvs|Gfv`k(-UE1eEb2mh3E<3gaY*<khckLC&Z;9X9VcQ;9Vx7U_jt}K-N zRKbz+xA&41D$BTx6*+v-?xe$Io5L5$T!jW%`s#S{$D`dl!0?S?@k$tTy=FL4HF)E~ z7R~tER~O3^(ph_Z*x0E^rrmu~1#5Je-IOV~?2s#Ep#K$viq4tDjq%u!6ZL#D8wb!F z&Q(oa*B+4Cljy^{7?9UNhuKOQhJ(Bsdr@x7cT`3RC`jBKcw~l`g|f{oQ-Sm4s$oA3 z?^;~W^Ou@2D<Zb0E5!*?ccDTYxFUW-h*u8SPSJ;ZLyg-G5<I8&=&xnln`f<@%2l1j zw<LjsYwHOd!mXyv^RdgjWq)OIxB?t|KP!rxTie#C)Eex<rSB9GEs*cEjr%j@{CVpu z6vN|Qa)&o26;q<(DCCx)N8LV#^y+BN&e{>$!pVQ<oLM$Ww>*?B&YmPLTp>G@s_CIP z0vR>^<Ew17;@ZMKNwkjNYuZQ!^5dZoF?{U~K@&n!zl(^c`<$fa?M>mxM4_E<crDeF z2iUO~K(D3t>@Im~@&a$}1B{i4KfdugTP|WK+vk9A6ZQp}$X#YidT|fL8qgo+lfgOg z0TL@;`?y5*Gz?|IR%Hm>prub+MQ;|fL+Y`3!|}Fm+aSmpO*9a>NjUzJ;qaOoJ5$%h zD~pl4<f;7HaLmfUbbir2Ab@w7{W3cqg_AmhbRE+SD>c(C=Q*zY9P1>f8#3LedmvEw z-XT5r=xt^gog=<<@dS$phgP{Q*!Tqx(7}kwX`*fYqBIe~0(CLIibI~)n}BNA8#~@X z|5a>h=ny%=8rc*EuLG6bWmF&bp2zEpo#f0YPC&0JfIh5yP)#2W-0V*Ov2<lhg*|?v zu)zqgw6RN<Rn6HRg41f%I4UqFxcq@5=1_&q6mJbXC~0S7tAYKV2aB>9iGIBUSdw{u z%Zjpz?+?z8#PP=A)f;t#J=-;2Hb1Bac`6SFkvRuGX#I|G)gaZ|NZq0{xGIZxZm}t| zBaC9?0-##T!)B^NQF6uIq>1@nI9AT$f!F7!%Pvqp(E0j4w9V9gNWfR;q8dANv43;7 zu=H8GcyQ!gYVpTn&_++vXmba^<$r(4fBZ)BKG;F9v`Q2#U=sxyOi0jfribWq+URDq zL;4SX+W;GOn>1ul>2NOoIUP046p1sjD~Y-yp*=w5fNgIjMhCxyBE;z^>AdO3%f@0m zDlDqd)`qairMPZoAkG-AXm$7A6+P(R^&bsR{eTQ~tQcK<61<6c!VO+P`5$k@U^k&4 zWW+Zfq*&EGxi~TOfwc++nj%5|clp#l=%9=jCzv3qzrmZ!a5s(H0<2Go*+&XPAH3g% zyL-C)I(wF{meZMZc~MU_#B|7q3c>#Q(nQ9<(!^p0gO?;i1$J69eZ@Xbb{0d2qS^;L z%wFe*9H)Ik#S4XMl#1*ECWE^)i1op&&F~OOVnfD}8^<SvJ_~_jPyw$e@3&ps4#Fpu zie9hN-Bd_od)lUB-@kr}5G|7v2OBa>oRn@zDz<_x|Eh_Bx0vqm;LY3S73_Y>0qmO; zoGIh$C7m1*Bu^?q!=yKP3<aei=RtS_y4R(PR{gE#$31u*tpY6|lJ|^3)0jogw$)&h z5Wh{&7M1X#Oy9GR&MnTu&sG@rP^e9(KF@|sFw55ayJa}ZEs@Z8N$0BoBzMMzKn&gP zOnGN4fuoqU0}2Yra85L>F#Ho)JF%>){Y~4|Tv~0(E|W0ON0NeSrKSNU_72vi+b()K z-})r+K7k0Xgber+_l0tFqqA6gnOE@Fp6Bvx(0D?~L7_tIzFR7iM}-2nuv<YZ>-hg3 zDJn!NxJOEK=()^-Hn@@*Qa^gr85Pr|QLiUrWhNA(V=HqJu%GDXZ!gT<5@d^1IB`>4 z0zXDh3TG0#A9}iR>mfxhYO?giCBnf*F@DqdNnw;wlhA!@)Wm(o3aLvO88YpC&+pI^ zeq21AI;=>sP!WLzq@e`Nzuzoim1L6p<s6*4X-h^Hu2NK%c?`;z1OYa@6ZERy_Rml= zi^iqfv(iu>$Scq&##tg^wMo4OnGM)T(h+Z`au;T8lR27bpm5I+GSD7)>O_IlHWFfd z1KeWwGm8?&T7+1l1SjEiK4(81%~f?2Xo>Z-ZaUzhP;S&1P^D8Eo8R-qIL#woxJqRC zocUYeU!4956q#zW-S*mPvcvwHdIhi3#>}x`H~SymaG}2hqaIOz|D3$x`c6S*WsOOG zZ*_bl(~w&}DP~89HyXIFGn%-cm@-ZeX!06-V{}Q6O8g<LU=Kmm4sHp%i`35WncZ9l z{}=ObW}w+Tvq`=57l39}a6PfKa?eDy>1qd3zQ;*6#t_w&4!KuB?KQ`IcZWrI-2^N$ zM1Et^HOWD}7jgA~Sv;<+Ya#x(N84}WkC%h!%E1u>(+y9-z>tFq`PgB%ue5+ig&eOi zgo6Dve??dV>*4h}Du}06M!}zfU_+Kt9X(IijG)qCHa~Qj5lSHAtzSJ|<Ei^8K?2JJ z134C-{ufT50iM5P<5ZMxqWHBSar6|r9Fe(kwM;P-f~lmgT02=XCgeB?ay{9tZBMNR z0rB@ltkijMd`<rJY&ADYcR%3VIEBbV-6md7Y&1&2ERZy67}7CPkDNuTcmP5F6LAAk z1l7S#7m82s^2)SOsBLmxkk>$_3Y!*Yn0Vfprk_~o@bCPqbt;)5PfNDJBfl($(mb`s zgAk@XG!cmB{5TD@lfTOI(d&Xtho(x9n03E{u1JJ?CJ#=tTTp=ZJ20>_fCw`~WDE5? z>xsF%bbbOqDzEC)n_so^Yc?+h&q&Q_cBYaFSp!WzG<($f9}c;8zaW1jlM73g?iTN2 zcb>3MjGm%YsJ-dGm~Gm>P1JA=tHIu*=c1%5P_O64%lqeE?~jMB-hBGP-}~Ny|66Cb zOHy<QVL=2!>(CFup#LI7h>CrGKyBt^p<~Al)#&lRNVMX1#29%xIJO)civ25B*L)TJ zEVKRKGEeo#`O!x!Gc7tVYsXtCs1`MPkZ;^%nt>|=*BGmz^`Z<}3u9nKOpu(YCU8pE z0{O*hZ)jhD*$dn|=DTnUn&z1qNlUmC-t5#)>4*>@$F|WF=t6au9Qs`+uOQeaZ%IG{ zK_DW){<nU)HG!S%C^C?LxZ}$Ji4bYZ_@zYvAGUsh<RDZ1&GD8;;3#$LsN3Kwe9d}y zy4LWg)>Eg$aZmBs?UL<%o}6qO`<m5zF+<{#jci2@cpY$`{P%YQTI$Vb%gg_1z{CDO z9eA`(hi?Y(U|@|-U|^VFNe)=R`FA}>{4O;ATJVoAV$6zx81ju6)SC8$6upvv8cKA9 zke(@2x>6*}JUT4XU490?KO#R+MPb`gO1Cbsz`E?(PwYDJ+)g9<{XJ`A5qY4HrHxj? zzX;ZXOdK~AiY+aR-M(Tn>~kzg*&iPRj~=%+75z0@xB1qS%y9_&EOlZHn2vgFSaYd6 zN{QtV)n`bkup7%p@V;_qaxL(Xt|(iET(ejawd#&dqb`&O3UI~q__q1^u87=5yt7hU zdd=@<d&FMK%5_n9%fHg%-bgAaWp(@f!VL8qS>5c=#iTyZd!|qYjU1GR2mcmQgi~rM zoUs-Y@F#ti%F|kprlLRtzKX|F>RuQL^-}p?31|wRCI^J2oHccf!Hw((#E=o#40$mi z$_}^8XPsVuv@o&dB}Qb@U;(=}WnECx)&acoVQqr0L%uHLtxN@g39^167qL3l+#mU4 zvb_}M#WmPY{I<&CzO~)bCsD?%GQNSmkT4#dnCktqVblH+6iztcIs@t%tHeOebV2Tg z_@$iK8LPgcRjI=PnC%Z&S>&bntSxi4mK_!q^WBlFz3CsznLM=vfB=qVb(i?yj(>qJ zFa%1aGd5}BgXnxEc+&d&JXAt^O(D}|?5^wKrbyD1N7u7TRZDyV4urO5#DM&`1fwE0 zS~OOnf9dVCWg0MD@A6V6YDieMfst~<Oi62kK~X+kDke~105z5x;N;`R)%CdYlD+nk zLvcr>#V@NJVTVE2QA#mvs4lUq@Pg@%H-bX}$6R&Sa`e~spuvbp$^u+<vXM5jId%zz zo=_;J<?##0hh#_;2NHiXS?Xash&@5sXo57>NOM1w2@F7<vrM3(F@Zhk;1=hQ#7LNb zXNE<li-KkNGM-uokA-)%p;F%ueyaGlmi4bw$SB2C43D@rsU#Osek7dYwZIOI=>}v< zGOQM<Kgv%0ow6$QZ>g&J^E|LZ?VzNS$7W}^zJT4vy#sW2YR3og5POm=B`29+Ndr$B z_^ee=tW#iPKx5{xBBI1LxsYYbBVVNr9W|7sY>P5y!OQCb46V{u#qLUeQ<g`&Z@^p? z4r+k)8Cm!@Oi_CMP>D#=mAaeH=E)nY@>NH*Z6`az-pjn$lVr|n)#+9$cj`B_Y<Nlt z9|1zY65mpT8{7L5ZfQqE8*u8%(?bft6jb#l=>*J^7=r|BsQ-S*Vj1e}6%9^KV{$Rv zl|s$abm@rVn%)kRljA9_fPahvqM61jDKH#|Ue*;|aVc5(>e~=OojnN*!N-d+X;?A* zn48BNup_nZT{qQT4M7v_J)X-w+LG+wQv2bp4La1*3fwgFCA7uMC>F0r!H_J=$&?%& z6#zm6+o9#U^5p>;Rx9D+LKvg<Kyq^UuztmWJulRh>x_6_BLQBL{0h43k~cvoA~Tj= zs6R1`IPfCSq+nY1#&7l}e6G9VfAoC`174<3RKZtmjf(C2tXk(D9@3^%$o}RMtcYLH z(^Zr39t9HgfM4pxuLw$94Hj2Y^APqBD*{*KW7}ZzR|<KUYa+CY1Re;~{pzagH~*9> z7!muE8jy;Q_PnoP5gpoL#}s~?*m$Q7P54{oAi8IXo{t_Oqlc&Eo{#eD<ko9~sM#r! zXydHlM?(fAaPJvg+~rMo&Ld0u41PJ9<31^UYe^$ArqvhKmaKEREeeinnG0jX>j#$i zukar{+nnRubmQsnCYi2(E4=n_mBu3dR<k!nZ`R9B5-HQcN`ly;s5cfi18fqF)exqg zL!Qp*m-o_Pyqv#ohZrCU*|IYdV7e6P)yyY|%n^mW$kc@S)hv~H!Ojy0|AI}!1MsY3 z>qwKL3nA(fvJ4&^XYLC_7dt3Lhyg5g&C`{`s4jeWSMFi9q16OdcJ<mHF|E3eEh3F< zDlmKTJohxdxy3s#YVo4DD;0+Fwjtq63CpgYMfFYULVz80O>S;D>kUc39Ms5Kl~h_K zJWWocbGPJhfC~$O>&y6#u<VjrLE2Ebid2W1*8G|p^<19(nHCe%26QRi0DxisG>M&Y zoCz9|)x30Sr<MrQ;?f9xJ(SDj)wMWEi2iz%P-S+c{S-7zP<1GOWW|5vDR!j2s*=iU zr`M_5D{xhH2x7F)j1hfPbIx)mGI$SR-Td}uo(G<xMI?)`DS9v4VRdOgcyB17U>Hlt zv~%`ONb!Be_rA56yxvVZ4d83qe`GH4!)y%jyal5kfS4)@A5<5fKS5;Ut9|ZE^bs_) zF`A;{GmVtVCx0htiqm+z|18iC)=55(_ub_*bpMNUCcv=~Hn{>}vPF|^F9WNCTwwum z6a>3+U(l9u-0*|VFaDLbE5E3mv4-O7Qy!*-%B`g&AIy?W%*vof01K|8gE;7>MPyaK zWdDAAzGi3};#a8gLG!qG^P)7EDMI|VJ&fb>coTQMU*#X`x4Q|E;$;t_H_SHsvB;<K z*>ag<wHH5{wO$+*(dz$g<$hUC#J}1tG}N(%nflwi;Lu4qL=Wi264zW8Ql5Du3?u~Y z>S&1Hud*x$*Uw?i2;_Y$E2~_;uATF!7L=!|rwXV$gz$_O+YRFMc9n8pKZ9>8_M{p- z@>c;dCvW(avaTigE{GU;rkaY~m!Sz22Z-C&@dc3XO=IjMASZm?MjL1_^@J(v9h&0Z zE4jnr=d*)7JKT)f^I|HW*wgb<5c6p^0`0)!99sv~?20op04utp=dlUeNb@QMMX3S$ z<al(n942cZtko7HKX0lYGHz^-(?COW1gCI(z0^a(8@L}L`b)(b0g+#pxC9Mz<JL@I z>)rxD_cPZ_%B!v#zV%WIO7nLN{ge!?-5HBFQ?X%!_LGn`G@ODvKMFZtPy}q}qiMp! z$rnW>mndWJfGB(}+>piJ1VJi7F(x%Uvt1jDOCDx(dTF|mnM2FhHQ}Uz9E1%}TLh7w zVq-sc#g7$Ue}6&$;|TU9E>0P;Y}ylwv=fzpYfh7N%E~RZJ5Ffxba05=vX^jx>^(l0 z#kmNNnAE4j%wkm{%_vhLK=;+nO?#pqv*x!d;iKG{CJ+s;TjyOo*JUq_0y(1<PI7TU z^Q(4b<9W>$Yt{jEAy_zU6`}hUZ;<5%QeUM-b@s0%1Xz8k^0H_1&V@x*vo^v8F^_h% zjHSF`Bz>hHr7*!)4Lkv=#^vTFi8~!M1xv`==+o!QR*+C-rXR)CHY6M*6VB2601K5I z_G2Wd99XB5cC#NX7ZD;*2RU|MF!ab%wSbD(?H$rFQx9a`%!=J{qxq@s*+dqc?~@a~ z$!p!gT5<cCG`iH5XsUV2mM)~K)XRHR!D{!75WXD8x1|!;$%MON?L5ebtBB3#B5rZ@ zO%U8)wR@DE2B-MmV)8jho(siNw+<#64zGV}1ZJBiZPqA8PX}Hu%gXEy9oA2`el&Bw zON6}jY^+|F|6U`o=l*#i1=Cb?A=SJ&p7o>IFQQe_E;PAyy?fmH);+^DB-UWmRiF!e zmQVKOkyu343O0)4(ck6YF~T~7Oh*iH6ApsnV`lK?2l)RdCH;RhQUtKaz&h)K&)(Rr z>EsYe62RVZjRkdVTY1eR;x&&#>6Zwq^@yhmy`nnjzw0#~WbN4fDK2Qfi>|A4ie|ks zI6ClSob9Viz+>j%h=lGck#^cVm;5|@+>|pFx{jOo`PrNohg2C~4ZApr6EvS_?fT>G zebbD2DR_&`LV#qEPg>73%Nf<G(>%z^kgGZj0OTs5Sg*^u0v#~PLw}%aqJ|pd!4BFk zqK}>ugfYk~t?K{EHEOXYjnW(zdG`k4Z!SOyexZ)=Ug3nKlZ1@mtZV1xqxlO(xNUD! zG2RKW`6UPSln~RG>~Yi&r|pC8e|oXy|Lev6;m;_H(D+|3cFTJc3lAKo&p~V8Y&r19 zo_4nigWfxA#4)6VI)gDC@5(c=D1S(mCp+Ae7gm}Ff2vfL?GbfmK{N;(&48(K#I^S^ zw3Zq=99d5@&%`{g)YBI~R2)-Zc>~IC->%NET2OqMUW?TRM`B>yCrPcEreS&rXY~2* zbt2Ae>0*u5K>o?d*<%Ms>>zA$iXW)O!-0`zYkV4)V>kWksTl4=4$JwPxH;58P*cLH zwgP!sinBtO>TA*>zhrJEsr@HETu(;+yBVD4hm+g|o(2qcCiCx}gOaXq50iQr(-w`- zGYCzgcA{1Ta*b6}mK}ZOzD+H7c5fG8@zMJ^*6`e4Ql1l$#Lb}<9`B*r9Kiq_Ul%9$ zZL;upnz%t6>2F%w4WG%;AEn@PAv<XsTo%4K@K)2El`(7d(qncK(=o7hR+9%RK5C(q zUr#|7`-V&qi+r#vLV?5a%+(TWqW%9eccMYO=Ojk_GAZA4;4o(=z}H0^SCl_4;aty! z{idH+W%lQz*TOzjFgxMgo9?CX|0C)D{DREM-HqGlNeVGEpF?p{cj-9)`_nRjex9jM z%Cy$`hp@v#{brZX!@u;jBr^8CVwxxAA~aaF8JQ^f5{-&vV;NH5A^m~5@Ac`njX8Wj z-SD&TIX)kFxq5l{c%J=2R{TL2xBdPI0$vTD-}mFMfUoC=+sKCgAJ1c^aR8w3cO4s- z=@CWr<LTw>=B)1H_3f+<pg9aq*)^jzWdU`SP^clNIaBWc@t=%MY05oFMtl&zG}BMx z_5W&(?T51XKm)8)=ESp-J$L;2;pyg0C#}|ECDhI`8t{W4=jUWanX~tI$f)G~G}=C! zPT^oy$A9AwnPeYK-~^y%siEISv6CixOV_LxzgRwsQ6%FG+1MatqZVyax|M9-@Kn0- zG8@YWSi`Z)MinZSJZ-CKWIKdSzYJ(CdB!x@(5O{_Q_v;{%3=a=M$4n?7&Zc<+MJjb zi(pBg7oJD<(3F9yMA~S)fo2EguGtiCejcnS)&08=ym(eEBuP5X01C<FMG|Nmv&o-Q zEH&#-m+a=zc!L9(Y<FDSV<hz9WT^xqNn|Yq4t-roE-sP1=96S!#=1Kd--XntT1;rp zepMr{vKNl&IRa;%h>hMfYA2ueG?q$>ILGaz(MA)20_n<jVKyz}z7{ul>F1#xu#Ut3 zl%a%8C?3xuDbCqg>-&5nEV64p8os}28;(I<d%aRH#rxXDK(1NRx&8=xr_pGHt22n7 zR+cG#<oMB^d+*WDW0&EVWKp-Ys?tScTrB7>D(^`it$Zeoq6rfhrMTlr8LAvSu1>dl zBu|1lS7RM$Kqy$1g?_Ta+{@OpZS3>48xi2`+N6ueKa1+QRvVoS!>hBXyW9vS$5)Sx zyG6OP<I>|4#c(??HMQ0IhlX6<R@!@XL&(+VeEbST_CBt{7~Ke;lG4Hi60q}YF;hwm zEkfQJ`!mlyf!@BseIt6teR#@XtAkl-4xSQ*%hu@0j^U)#K09-ZsgoA0nGWq}<qDkY zrvK;=W(l)^eRVLzzRzf3f?w(clUtbge=$iLoOpY-mHFFiLHgC)sl6-7h7fs-E%_^1 zeWxGk;`e1S8Vv++8bLY?mqA?883!*z@gzN|wor|b{@qKtMzNI4YSh573xvYfMl7*H zU09J5<DeYWFFCXaIV9pon7C=dpMSLdF4)bhF86)l6kU6>m+6$r_B&>!+kfWjZi|ld z?g~ug{6s)@{~f$UhnPCpiigd!Rf`xtM&jc)p+X5+SUmR}hL-fI)tP`r>e=2UrWFy! z$PeYv!aQt;b%t=k5pSiAP*{B`R#knEL{9PawILx2m1pC-toxAF1_m(%um)g@<yyL# zDBSXp{!vaf551;5PdKP$|8Ehnz89;%YuqwYOLtXK0;MnHi!Phe!ie0m*2+^cPi4fr zcyl)WW*M_i_L!9O(M~q8%~Q>mY_xxmsbdJpo@{<721|TdU7EaLlzThXA0bQ0w#L58 zu&k8D_V`CZxs+3>#qqn196+9YyJhA}?HaYhgnz#+@8oc10Igjv1nvsDj%avz(LR8G z37upngrf|NVR*OT+Y?E9vT9+#rOh0gZl2Kx{asF?yzbiU8V%M3>0*W2=zviYwtw<r z%85k&D5WN<$cep1n!Bo`MHTw8jX4@``hS5+h7gxX_bvE#fE^3(T}+*z%(v<0IJU-S z_u_tp2XobZ9nen|MudVp9>EdH8ju*W`9uzxJWEqw=5mo^(jhLrugdoSh7?1_GQ4Av z?)<L@ea3V{;jCUaUA-va={bfw4@MEYz9b;CP!3y6wlLhXhREn8dpzUNF(2L*EnK=A zuuOX#IgAKD^=kJ!c#RSE*QD2FOhQ~Tp{Wv))saHnkddO;ki}9<G!fd`hyQ*a3|J+N zE$!v##_#7hOx4+h+1g`Pwt%O&8cAn+<&f?=G)`$W&pRK^PYX81pvQjdC_Azj-hmRX z+jt9lKoatKF59ih*xv_L^V*-G$ki=?%6J}IEc%eGQ9xH4!U75PsENm|GCq5ikhw(~ z!os%P$$x0IP|ac3vIh@!uY&pGmX<yt^<lq~vT2nbZKEH_jo!~wkc6OZmCzr%adc+8 zX>?81W;MA~pJ7=K_~zEQhqv~ifmT6RtJEfDD$&^<t*n;R;dqZiLyl~K2V?>6_Y_Qc zhgTIT>Q@(zHMi_e6Mn2qA6jj<bpcXhvZUk)1|?aRYy{&;yoZYiG`)(PKVleSe?6VC zGkM{e$p?jef(qSqv{}RC4UT9VoiHyeTs0U$4bIB<c<l$5(?cBN6G3=?Wwg}F)1V(T zz{490j0mRBKj$+wv)%yI2_cg=fR{Mv18bt9MHw2}W;*%)Kn4U$9me^o_dGe?Y%{M> zCSn~WDh~Aj#Ozg2v;QW(+yS9%ob997+oId4q{v2Rre^7HjJkg}(K!(}D&8kqm(7h6 zGa7de!+lwt5iay#DeZq9;%@tDNb7%(f{hJH60?{c*`G`}0EmYeiW`mYq*KEQJt&v> z2r4Db9N6P$J&&vtHqGJwLrTFuJo}39`gC<WMZq`e)l!cpe<3talRiT_?x*!I%o46l zt@%x}CI6X`rysJC4g51tsxb82#!kBSzuOy#&P!x`SY}o>;6M4X-|%>YOD>3%B>aWe z_9b{uhQ=Hix7wdJlnLT$qD^~A{y7a%v$%MBAe9{kE+C{Xm<PjaZ!xPg!I_lG)?6E_ zWG*xJDTVey&c1C1vMpSU*q*+l1j@lVTur>3YhZF-Muc2;MiM`F=O@nGnnad5Z<JV+ z9h$AbZ{$0VkF6GD{eYj7(VCp=<l<X;?$nVn--H78uX}ahq4k=*d`i;<kQ&dz{<G@S zC?sQvpnKY4$!seE0b{DwN-5JJFP~9{&&hanS8BF;Na)S2z7ynDQmN*IY7E1T_B^k- zyhE=KdyTU7S>v{Ks-gWChN*q|;|K_Ng84z(!KuFTJe)Wg>7`i<<hodd$N#5|D-VaV zd&6TFp`xY`Vu%Puw(NV^TkM1S#8@Z$7Vn@E@tJ1q`;wLqB}<H5Bs)`<vczX!Ta+lu z@4Q2)@A~=U81HkI=iKMM@8?{Xxn8TWq^jpos#A|WhuN`9d*?dq{^7kN-nE1hEHr(_ z|94%{@wA-j#p2G6x2$u*=a-$TKYdzDO&odbtRG&mKw(2+!Zm(Y`&OZ<7WJW+vq@d9 z_D+RQ>V5rOxUiOikKVOW4tk-ugUAJXrSUWCG|<kG%P&0R>6Y}}qyxk(On~yl)1f&) z&X(b(iSFHA4=WT3k1)5jk~`M)8UuP02;D<cPl15#FUcnt-0i6p<7#VdSG-b2J7sUX z>y=5W{Vr~=Rgu@JwT32{nmFxZf_lDSCazI0NSp_0VQzSDAzA+EOY)#sX28NY%kv2~ zbpcQ_D#b~r)?NJbVS%2Iq!dfHqBBwazGK?5bW4UdMNVZ*#;T#C7IN*Lk=kGH=e0d4 zmgZ4OqmCUuP6b>&VbCO-mgA1NJ1@5eVQ4>ru-JIM&MX2|XMsGoSNdvy_^M)n^-@7Z zVzb)|k*ob^W_gwrRA=2HOgIty=O0gH;b}b*{7y&7H7r0pSCs>nEz|UJ$NiZWeFnbi z12$Q*DkrMEMTv^^k{vSI2w9!##|CL9?4V_d*XL*y=-f$<J6#Q?E{HAA_sAKzap*I> z83?|5uJgQ4sfDgqs@-ryP}T<X(_VSQZgU_bE7|#+1s@vWx%$6lOqLEV+&ndN)f0RD zq!Upl6brpm$R?&t296*&ThB5T)(IUTY_xia78G9%%aK|%=5d_ozWDv6?>yt8m+j$| zwlW1=GkexuW1aM@z-tX{m-^(5Y^s?2bM%X~vb3F6K1WAp(34)BHSi9$qym>10Yh_T z-=G(T`WF-aW)*wvG9uM39+$u#*esXXD>uMQ*P{!Wp2HOK9Wb#e(x1+~2r=zt^}0Fs z2xYa;W#i*}`Kw*%+j1=P@lEOkS=+x_6e?~JilZmKB$nKophL^GKa0{12!D_^=*f05 zRkB1Nou++cCE^ud&{1^^DvrbQ_@*ZJ?8)`X`7)9nM|HMmfjG<PHP&lh1DgP3asS7E z0feE_N6fb4_3TAgGKy{Fm}-r6+(d4tgVF*@!EMXwbL$6TSo$Gq)+@=erV@$K1&RlH zlgoRduI`E{Cp=MW#1oXsyEFq=k|UD$-zX9GYIFWqSaEMJD3gMcZRjd52e)L(x!Zs2 zrlIMop9&lIl#D)3nl>pDj~Hl%tVlDC|Fpm2E4ML63yF%9*4oHiNK6c93;b8uU-fXA zQ;p(ZvE9Og5XDtHM8<h&wBs7;gzNB8Vrm8{X1U4s{d!kWix(A&nu7;&@0saatjDi; z=`I^lYaBp#D<(d<b5)6oVg1}Rhg!JFRVAqx2~<{QtMxXk=-9xy`B*5qi&ABBZ%agJ zanDi*9gZGXNY$}GbF$HlW(JT0I8p28@|d&l7@xyjOuRMW14I$HOS@r3_<n!hih6I5 z9ihtMD$r-Ln@e8rR*`wHLC0;~e8BL|73JFGH^^t+ad+}O8h`wV)&YKM(JPQhCryaw zuuQxmVP^~_big&sI+iDxC>E;ot}?^#RMW~CS`0WFd;5=%j*j)Lh~3R;-=LX)_-~+h z9LY~5j=k5HKe$=2E`PJ|1{Gy0TZvEl4h+xU5#+Uye}^rQbFr2Z-4&C5KCNY96jN|1 z%eX~7Py#^v^s%#}f^$x?3e;ZAIuBGa`Dh`Te#KeoL~GqXp5n2<xGS!A{gn1HEQkfo z@ngnXYZl1KE=c*(AMeJLiU!aFQY^)Ohbb=6m!jPiD*i?viN-u8b<IAC80fF%s95+h zg^uxB`O<tk3kD6H7;vwzYV}G+4zmt<rGwi`w*P@z9fJ@_g4y<M(lm5Jl)}}TGc0n| z)e}=O7>5d;+G_Ir(~y8>0i*Cm3r$KqUT{n`;YLY80>)u;(n#Lw-G~GQEtrBzo)@VF z0Cg-EcFB1wF+ZFoxU`G1n0m+6_+f|7qcc2O0hxD;w%UWTJkWmg?D9z?@{QcFK=0mr zE6{SZUhM<HM>1%NGg=lN&8Y`~^S5shJAUF+7J1vNHGe7O=rY#!AG5L0Xw`jqQ9Kvk zh|f>!ZeK~urGg*N0|r<4945>e{=MUoQp74vMWQBAeWmp@Loq%3+6_V_rXcij{0{9T z(<J>QdkgxGFr(wQ{%|M>eg5!uo#xv*J9C2feP~#*U4AO(vxn!91kV}ON2>0s%Av#i zBg3wSI4j+HgYQ-Ix3Bg`?X1Fh+27-fV-45WCYkkDifSyaN00D$r4Unl;(KCxj%Ekl ze1=JLnbVZA3O^>&csl~c$<{g}ridc@L%KO1@tPAyBDGoe$&UQ{+#<$M`V-v<{G?f9 zUku#yj5H4c1NuJ5kIeC4&1Ubsc36h&|8v|<uX&>!%opqah(uD8{r03gK`@1bup;)0 z^=JgrdTiGj=?p>291vx3xL?AHoZ&@MPkVm9d-*p$6nV_j2&^Htokq$bCa;p2D3$6l z0(jh@l?ldK{LD{^L%U?=C5R2evA#cImJCs*gFI}K)%CP{2pBsW#$QY0HFNi;LwuZ@ zA`e#%TdL$=c<+R3rovxaX&DL^&E`7t^}!up|8C>bmmB423gjN<Sq8#Cbsap2Gt!@_ zaVX9}D%E`kL(FSNl0G@v$e#C{$(UKXdctrIGXicV*;KVe-4c;KShnPNZ3#re<V_m{ zeyh2+M0YP1adC8XX%f0=qguyr@2T-y57q1s)o-TgK5M85$yN;U?#u}$F0H|BRvI6b z{Y?Fhj+e+4NL7RapK)-*bb|zsN<9rmZ6=yUdX&@ag3J&okh?HIsGWH>!c$*bm{wOS z3C5lhf_}vqu=>&u7<b#X1^{4LY7l&pAhm4alo!0`dLhZWkhl7-R!D?2D{k^U1Xgsn z58hYlC>-rzhE`QkIzK~?*KDjrWQlH%1zX&haaSM#WY}7IgE;wGSr%Z_`so0EOO?1y zb%UeekIxW|gb?@*_E;p2&^6^;p#It}8(Q+w7C)z{A(6#<=>*6gCkPq@0pPoA=9q)= z4WRnY7$uK|;fA3F!$7(gPHqyC!7=tFMz64mNth3eb_hMdpH_G)pi%wtPA@JMZKJ9p ze;M9790NOq5O{bU;|h)U%hccU_OOHQ=9KMQI2C|3<NF^m>)S5Au6=2a>@f;SLgRdX zgZ%4+aKqV8y!$OiUwvjj?BkAzq2c>ld%Omb#buFs{oNrzz0dK{og^~WISI2CjQ}h9 z0NPN{g-IZRhFoI-%qsCFkEOSt!!*%+^@aL}6?Zz=T~=n3Bn*MiVE`PQ5oXYbYV!n; zDWHH8(@wrH9?LokIcPgTIwQ@bPhcW33#V;z0EWQucwJf#XspDAlr~I)OaOP_qlC%x zRqud_I}B_=IE<J%0Z>boz@;pJK#`nO1U}e$&8ErzxcvPH_GTqN&KheDctyH|h8gV> zx)Fa%Shi}O`r9EIBG<3O^lWsb1#>Ss(*79QwLl_2X6q#*I2lxzP5i*zvc<}~Mx;2P zfY3Q9M5-UvcMJ|32aFKBe;^0l9H*erhXI>kz_FLELpArQU;=EbFXbsR$?^jh#x7_r z6&7A4>u;KjrS<Lt8*r<^ZXGpD^%<B0YpO-}4cr#WXPRvbROpw2un?fZpCFBKE%*UB zW~I*?0Eq3*xWkKrhJ$If6wt@&O{!81$nz(!0J&fnm|jT-qQ}Vk|36KZT2%cGDvWIE zQ{)Gh@c}O=cd2gy19+3AIeT_idzRRvq+#NIO=s0m`kJk()!hM6mTfdBaxJZ??@he+ ziM0^jVc@>8V&&Pm3TL-g3v+xFX)N5)Iq|Gg37C_cm^;N(G%2URsVvYk(jT6GAB%nq zZ+DPw7{)k36QUrUjchqi&c*ol(Um%u!^Fo40=N)-FsBf$0f3FQCjPML3}z3(7WQlR zpfK@FIRL`3z7Yc99kf7FgEn@~VKu?XEM$-P;Ga>?s`r2})wrhxkPNU0gdUu#?p6aY zM)&1gKrZ_3e}N&dH2Xbz2kX@!eIb6w-C$AJ)XT=a0~Unyy(B}i40v7?Xj(cSgnw!- z51@<-CT<<-wuOfdL&^$K(;kH*)%TsO{_=XecDpPM>|>isfHCfC+*-gnF#?w<O7naR z8xbrmd>SmM73#McFwuluVGsJLn!wQE3-WE9;u2YfAqIdt|8?;9;=^y~0IQ+Lt<Y~` z1H*>oD8pEM|KBj|d{qXQ8`esP2E&aIR5w$XRi~ItH4gi52zKlB-v0m61&6&(301YH zz$)1(PxMs{0Tb&3kS|b3&%GicK$)Vgq6T*T?lu=?*Au2*V8b{;(aw@zG55f&n4amM z-m}e<=Pr(UWM<6hg_4(ltk?g9;x9|LoGG;YSq*{zPQp&;doMLOMMmWm`9m<ANB2&m ztOb;TZik$Aw7hk;PC-<oVcy@X$wC=E(x7x3^Lq<=3w%B^n~~kZyw;VS4m-zT$C|Cu zvEv*cSX52}ieQii>@3h9r=}|{hrQPy5A4Xz+sQPg&b<9>?p6V%R3FdHZs8Q*0Ym4v zz}zZ6DUR@sfaK)J0iU>m;2#X5QuTZbYBtvX4XmZo_T1mf761Yd1=(uDWxMow?*Flf zq(Nc#Q94k0l%s`YVO9PjwSi#n@ZvJ{;34uKdfav+)7uL(F*2eBxm2KD@5=_anQSBV z)!kY)^?tgGuzt`C+xmeh*Mltqm7k)-YN-D-KF_MAvFc>b&u(w&O4Bo8_W&|i)Pqk0 zzFJI*N0>-gRU?8JSzY;c;cJB*uUavnOQ^4C!zD9`hGe(R;)6<?_Q(&3-hibz7P?jf zW<ymT3k+f&>Maije98Kn3DT->4zq6+A7eNR@LZY>YwRt?21$dWbHd23s^LikpM~e! zH4;ojnL`}~S0h5KUCMPdByhesxLQ5sM1h)4BM9WX<Qv}D+e8_A93W=b4)T!=$%0pS z8lJp=a=Ix0{aA-g6(ai%^c!g;Q(oNgadBszrCw75S5J^Fy$uepNh6^OYEfWl<D=Se zsi7%QIRrWVsniKvTTSTh8>9e`$qaorBP(XNFgZ&c^bbXXD{MWpJ-8snBISzS^MU)c zZQYf;2If74_?wl8BNk3lqR|dU5PGb>HvBqjYsPK@#uy$bT~mL_c^n|qz}&%}lz;R{ zYYgw!#HO6psyCkvFFs`Ee(i_HPd3}W6y)rsV?5(N1~=x&wUrl{qjj75qah*d-?#q% zO!?QIYp<SRqvRg?v3yNs#6#Z5^Z7CM-OofJR+D;zd}ZrAHS0`X<4X;7?Q29{DIP~J zkxv)yrgg7Qm(|)P78cvYq>ldUAiiGMR>MZ(y|8ZUjV%76srfkUpdLzXUp!;vh2p*= zR!=QuTw0<Q*}v}@?|H(mx^I6dW4zqwBafVv@tRtE&+-P;zMVSFg))i6w>-~v&lph; zLqW%=_a1vv@;c(8v>MzWd7D%2#mO6}V|(|JD#V+*iu(8>qg1b-<xzL=!>8|hLw0|a zbXj^qCj3j*gt(?&RZo`N>-G~E&V+1_Bjk8mewCL$YGysWh76_xWQ|@d)W|0)*{sgD z<~t}GM!Z-(haJsLcea@??rj%Ph#Rp=u4CuVPYEt;7pOPN`u^b4T!`?MLn<Y&>|1GR z*ZeAWm1H>yQ$c!-k{mZi={7Z5gHlV0bLvB(M5S9a4N>D(y6)wdi8oP$E2F8tR)wx? z%0e^45;N8W@zrA1+J@Kvm>h}{2%|Qx-WxyYFk5CG;U`@5w@Aahw%SdFB22QaZJzb{ zw+H=YV(2H16@?xZ(Ia*Bbjcf>czTV2@TTQ-9J0MxcX|UNyFPK)Ai7Z<z25PJ_64oi z?$vtZSWRzDCT0GW#D@&Hv4hU|thDDI922&WX#|#@hpHvZU71o?I(>M;@J$$R6S=0m z592x{<Ls!hJ5#0ked_(hY-jU_A!gjh8BB&Gs%cIl@}kAbKDyY3sJ>G}C*sE=haVR^ z^t)~Jw=*>b>-c$$4^NFAJa(9edhZ(kv+>;B>P}O(=bTf!&WuYMs-Kh~By~+o*M6VX z*hqe_!7Dy_q7Ay`M|UJnY_{{Vx+2vwM`m!`1_P1B_4RMBv3=(%&9(D(Oay`j&~)c# zo~Y!eK!qpzy9sgcysr*{IJnaVd}2F9WIn`6yx_Wr`_HfW{@&|=BEemVd*?eB|DEKX yHc{POllqzi@s_(HwT2V1*`1#oK7B**?F}Dl1Y+;+vmqp1h_qM%`Z728>Hh#Ur4zRR delta 21918 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 YWjUpfjdT}9MLNc2yUXziJHwm)56^Oa_W%F@ From c09e7039384d08dafc4f3e8c7d6bf56b5acb1f39 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 7 Dec 2021 17:19:01 -0700 Subject: [PATCH 023/112] updates for universal gene identifier mapping --- resources/resource_info.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 5f326dfc..4d04ecc0 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -9,7 +9,7 @@ chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/| chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|2;!=;NOT|None gene-disease|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|6;!=;group|None -gene-gene|;NCBIGene;NCBIGene|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None +gene-gene|;NCBIGene;NCBIGene|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/OTHER_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None gene-pathway|:;NCBIGene;|entity-entity|RO_0000056|http://www.ncbi.nlm.nih.gov/gene/|https://reactome.org/content/detail/|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); gene-phenotype|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|6;!=;group|None gene-protein|;NCBIGene;|entity-class|RO_0002205|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding From c7f9d49bc9ff2af66cd212cd961b675e7c49b993 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 7 Dec 2021 17:19:05 -0700 Subject: [PATCH 024/112] gene-protein/rna --- resources/pheknowlator_source_metadata.xlsx | Bin 28879 -> 29287 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 499922d52d781623c245abb8846b9784c99406bf..033e64d22f7095c175c673d897323808fc3e25d2 100644 GIT binary patch delta 14022 zcmZ8|b8sNRyKU@^ZQHhO+uT?iV`AIb*tV06t&MG48|+3ezk6TZ`|8afQ&ZJFUDM}$ z-|4SwPS5Uvo*aVKQ^CT%D_k^Wf`fnzCo{uR10_XZCT&?i)LSy28eIAysseP(vecK} zo_i=s?5V0WTANgC>-~6sy54jgzfHDQv{=!4k8DVfT9ww$ah^cht|Wf=W#as*eN1YG ztniW}dk=m3mma+X@a;I=HSfR4fCse3|B;<w6fd8pTTl3i@n{asNs11W&WPeT=M#~c z1GG{mN+r{*y~|0empot!xFLhnDJHEa9tsL-HUnXF(P00)M!CI*EFYHjaftJO%Sn7F z1&_d}%Jjt%ajO|0ta6)#)LA0-i8ipO+no&t>;{Y#9D{0#v^eOox=%{@!M`_?`4tmo z!yF*dtP-B=vXeu@{+m+&hEyH$ETg#%3QYQd4<0iWnba?zrXnXj%dkK9P0usU$Z0OT z=D2apyX{C9(P~`iDtPlSWfV&k&l*o~{TU3x?({U~xHNs?;qByJ{VVw$l3`qVk^<W+ zC|r0Jo?c5Lh=tKsMnaBU!IQ2(+{d8zyaR==f}2~vs|5Haul$|<7VCRx;vtF7BMg_g z<~s4Yv{dpooHDR?xPeE*-rg&VrQF4LkB|z{i~cIvt6I`Xw4opJa>pTUu#lq@Xu=z3 zwi<bW$MS?a$hzQVL+OuPe2j!l*3Eokp0Kn{4#zuB;50P1%(14(w~7F***)jU@hl`} z0N|GP5Xsi_BHrL^{c&_xRkMHXTMu$ff!Ki7U|QI`GX`35xQMh#s-)Yjez|GCCO5i4 z#xQJX#fxlETPA*->&c{%r|0ekst(E#aF;f*GlO9r48>mQT#k~F{fV3Ryaj8yt86iA z`fd8{q=S<&mS>p*rBW-R3XD$XCRoqV7bXnt7TGQqGS3jyaH=42(L(&O!5We_$l<6k z{Q!f1N*I81pPJ0X;SS?zGZ?%BpS&g5U`3^BIli{boiWo|QuQ+*_&g{l(4+bJ%P`79 zB19i9NvNOeCM}KStlziD8^U)dIo3dk(?0*yoSO|FKSL7VCvLAa^J>N^YijE<wmQkk z|33d=?uDJgCE!E-U1&y(!}4`$(6mEub>)v(UKmqb#l-8!835l6{>UM~!rdjr!u;30 zr(2N4oyAB{(7Y(|L*=R#*9Ko8q!ici?OQT3KYG&(0tEtGku2yL894|D%USX-Nt|S1 z987>6LcE+hrbd-R<3g}D_8>q4->Q}7xAmvrsG_Kz8<sLGcS8eF?+wprraqDQkpOE+ zN40CnJF-`GY2)rAuawX)CTN`dW)e3MTAe0*f=tPkg?J)LpNdxR!Hl~A<qLfK80!&{ z{If9FLcg_o+z*-()S*7*8t^S@Nw_u{brAqP%$gXcPDKv8$`!kjp2q@S#v^E{G?ZEs z1^nM*7*}T*tT&<}a?2J6IIYB2M%A_R@KWDH5bV_2R*Y4G?LhxTJ|{r;C4CurXL0zL zmI+d$XrYfqI?P{sTmGQNh78G-c6`qZ?U%`4C2NE9m6W(i4yjYDKE=W8MN7*y<^&9E zx(_7cK0>%2_Uy2kpgaksf}K)^g6&#|f+cV%A1tyg&TfyHvJ~MEF<NW+hbQo-Z(rDE znBhv%Rz!q&HZHJcb4jY8HCb%1e+DH!lb3kr4`CvieZN?Esj?rh%x<yoPI9qzZmVYr zNybxUyLtI7%*LIRI88t|{ZpgfHViP6{#Fns)aav~QIyIq3D-4XfB~*nlB87auA4)C zF`#P1<Uqpkgf>Gg^RMmU9LkuTo5%0(W|v`Sm0Mjm#+)5vl~Fe{9x{R<yk?St#jo*q ztdSKw+rhNHr`vXJ#*9)YufA#`omh3vz7QiNr&rW~Z7OK2X9AZ)D`)f(?oOZ)GoG`I z>~Jmz>fpVBpu$2p3Lo5bHiF3_z;rZVm8Ed6B&OI*_|h%pql3~o2MwfQ1K&(YA(>f# z56L<_V3mw<=BWOw9eX@Ao+$~G>8`cd`e%>qFfd{BI^#AZ1Ofu``T1YBVgmz5b1s*6 zI&cAsKGT{FqjSznP<p_6)0X%{+Kzl|LAdIyA#`<Ka-Mq|ATn$m)6+Ofc;<XJ#r|1I z`Wj)LV|v&4%k%Hi5AU8HpB|sr;fICK9<Psk({|5?`}nTW^jjd$$e{c0(c@(F($oI* zds+HQ+0KW%_luVutSEqx-Ss*v^!D)d#|7|4uulgJ47%U<{ekAYrSFH6(eK0AzdulQ z0l??)uvKsG`+k#8V!LEyYHGVk<n&0?X5m<Bs%JQ=58WMyY&A5Ui+G|~;tPGBX1yMX z`=BLMr3w`Y?hu@&ZFp(~H7wUaHhF^@Dj}`u;JeH=PU6bRt<~$x(>qbGzwg_{^z+V^ z@8d$Re%;6YF|hwWY_z>u{qYz*D;NhXXXiQW><IGbT^sGZf86f<@dR9Mll-@gb~D5W z0C<pBSkT~`PVgNSs{L1;T<`h$05+NL+-(lW7I<r?d}i!{Simfk$7~rAmSe6GQE#_r z+sB)Mwk#8pNrOK3_Z6J=mawPn(fG0xlqI5@yj+21;LyYKdvZjeyRIeFI`ijSV_r^Y zQCz5|F79ti*l$-eB64=^XCE^W#H+Bow`DR958+C{=hL6P><ug~4jC!JUUJjh>=Dyj zyw6(y4TM)C*K~Y0djATEq5~~n*~@oTtR^fAhvP7A601F0*K1F2{`Z&JosW+{Rp2wL z(Os{*K%g8cRJ(N-OJ69F&(jc|8@<zfA?5bOw=*b-w{piOX_2@LH^O6EbR9k=cX?9W zn8)TkZ04Xr0lXNM(I65A+?a6_3247yQV>Z@8sq$#^^SzP$cTp$8B-2L#fQm3U7pwU zc;3DA@D5G>(R%mZKfJJ^u}(?O;@}i-QKX#Rzymw!qSy5UVz1F9zf7-x0R_r5q0P+0 z@4(w~G!(`ewwC5yao8LFCh<%i$&zD!q`Y1{NlMJ_lii*Z5;Gs|OM!8uyH!e*VEbVo z#B_pPNs8!N_Lf}=((PbYy6sS1oNM$j=hRj|>Whg%YF4PxD6Ck(_(ammT3X$On{r|s z7($WgOO85vJd~A_8%(ENJ@%{M7|W>`!v>+-{&5N`QkVqJoapk6m|I<ijkv4#-9C}V zHs^ayw~|E~7jkL|iH&-g!nd^CF%#sV9tPmU)|$26b4Av?uMIO3Hf=Gp6=6G7=jr2` zS`_cL{(zTvV_33;nFE2s?d@1MM7KE=h!AFuFX-Rxspe37Kp&)Rk4pRwp@lL-Zim2U z*I0+m8BM_Q8d^VHau@Dzb+DS)`17+2dA&z%!CBBS!obgKF3KW$j2$P|;2NJaRBomK zrz8nfxS5z0sxzzXDiUktjPD-%n}LnZj1D@el3aB_83${V@p7TY-*1+E%#dnY0MzJL zM_7*i!Y?>PcfQd0y<nMXjfh)w<XVs0GO@b>QmXhZfFpe#tpNeG@ia0c>regfg+heR z6nZuiY5L$Y2^<(Bi{H2@x^KLHAaGR1rz^#KzESU5sX&R0v!YR#hx(^!Ke6;6{Da%P ztfWGZkQGeX@)V*|Wzdup-z5h;@psd)M&R%v@iMdW3=I4~960XNcCA*nXKNWJP415F z7fe|>xfvB%<wUnJWGttM#>)DLUy4!xNb(`FdPE3|`>yJVPnb8$`o>51kAewJj==mb z$!jGqG;lj&J#01-3lD@KXHp(|UE{`mhQ9S*o|p>Kd1$Jp>q8NX_ZtRgM;@IW#PL$? zgeeuKTjEI|Ue-)4vw|gU|KeBw)8SMFVRq~M8tgHIL1i2?<J60-5TnIXT+f^mBGTf< zD$^q3yTo@DalT{oSwxb;q+kk_cS~_P8++}>99Ya|gxoZpCx;tqQS7-|Z_~hX^x4TW zZu^7Q^9%Y#*$!Fx0&xJ*L9Ua*l2SPaQ>pPf>R@wiVQOXJVe9>3aG_|nVPBbeD5)SL zMxFw;;T-l`VUmK_VJT)r1~WG|qs!z}-?rnW`riqST{^NhcwY-}-=oSte~rNRDwtnN zWQ}+I{^<*Z=XS>Hq?ZIix$UMD&Jy}$EQ78&*}A9EX{ey1(WC%)fTMXkbR@Vf5k(Uy zcw7Y?TQn6;w3!k$s#SRQa1yxnUIeKR2<xQ0Uf^6-=+)~<-VGs<q5tdga-Bb`a38}s z_9m@|_Oig8oO)yXQK_bE+L$nmmN}f$RO54-xa9>>f`nvnW6e+A8XvI8@<ZXo)xy-Q zX&di>zh;l5Y=J;fTK@*><n{wWi-pf~DHXAHdVp|=5!Z)*vUE#30}80)7u3q~<{~{; zotxIW*>Vay{L9@8cr`|jq-HPn!yC0yw7w1j$f@*+$)%JL*u(*C7M5>ejutlC?gS2e zHByM!-B*~kd<xGwo1tZ3e9x_Rz5#-fjI`VCTWAL26hOG(2#DYm2E#k_y<$*AKwAsz zrv1=M*Ok4FR)E<*6-!L5Zu>W}Jb`G6qJlX1!ki}=`2YH-(waVTvZi{C9tJ$(-L!wK z?MvPEN-#~Bzv`Uj6sAu#7qd+C6Eg$IzNS}*;Jra!BvRqMkqctZKoS5&!NFmsr(zqN zbR`eu=Ue<~rDpS7@u1&=URd7hWG4Q(8b_sehWoUlG~2sXV!L|B=?>E3HRoh7kC=?N z{TFYIbaCPk8RgGFLU_IICVcv&imfYO<zDEC8mZLTs=2F;;i?4uS74bO(oQx}(m;9y z$&3HyLu1>`iIrdC3%^ju>riX~ztO)|tJwlVD|v3@N1(gRl30M$D39%S?L(z^jY5`e z%cRof6e?0D6CMik7>rM;2Fv!>$OVhz3D&cT13Cj7UphlRMUbPGPyAn~f#o_xR~wVi zxfm!Yx5pBtm%W;{m@}1J(34WuET<a2&m$6FkuDIK!tPyBk%<#$<^=O<+%P78ivmn; zm!VDWE_?-v#={vN*6|xpJ!=2TGf$x({Lq9|Oe6w67kVaa^<w4+TR<IES`Q##Gx@X- zor`Ur+}_dihH$b<x&#Lo3G?#jE1FFA3C<U|jtZAX^qVVRe{kxqZb{3l-koLVqRXf^ zHsr8#hz#jNzc^SzZ4Sw&BngtO19rhTgliTN)pgxB+jb8;PnJH*Je+t$`AVeOSvS{_ zcAVI2(rRda2ts{QMKI#atyI_Q`+o5_L<*f?_$=7#E51LJn9=`}D&+Xi2<px3i>J{f zn2owk^-F#YZNBJ}a{H$WO3UQ5JSnQ$b1_8{H8`x8nbxD^Y5gaK2>&(#@+R`Q?LWoB zU+WA7%abKOgN?oNmv*2x$Na*ufZ}dt+<N8q-bkK_kY$wWcJ+C5!?Ym}i-37qCRGz| z&|PeEm^`Yi>Co4BuCZ(O*Fw#t;MuoV5+$cy3z2rP35>}6c3U$Q^IW4@iizbIej9r| z_7dc{<+IaC(CD~@X!RG+<P;PZdRGkgA8vE-abZyzFJKapg7-te`^AB`tO??47RDF( z{&phyC73L37EaYK%P--A&x^dYJoprZ{}G2>y(WvtquMWVs5#e*wnz;F-&8e%YLh+e zx{A~;Od3=O%FvACu4<Vtkg!}A`w~t2NQ>K6(yx4wXy3>MoXBW-l*#GlCs3?fD5=+G zAmDVN9OdYJW<BUxckO(*KU<yrbI9D=(&{8CF`&MpzBTiwQa*O)Opkp>VvB(%S#&Js zGF~JoZQ^As6;#BR{K%-Gi6fx>-bhjg<Mbs{du-j4Ty{V7=-(jh+epLH*?4d;Z^(l^ zExXHHEj$M7eN3ZkiuKibWwWwQ%|Jl5F(rtTS_GWioV9oDWwZjw!pK^rIX?<!pIVdT z=D3pr`Xm|~=&~gx9pUF9mcAx^Z9fhI+V(|b>(*lkv&wj_zqQRst2mU=_!yCOia?4^ zm<Q|a0^)6}sg;af*9Gi<n1&Pr4|g?Fu^njzBrL%o8G_p^(%$K`r0mxPbLP>YE@cZv zIyU6;5HKq=En_0}JF5)ZB)$g?9}?Qck(|de3G|7{bh((=S~1!kJ(unJljD^c#gZ1G z;S5>~<J?H*`d)KlrrO=BWMv^!$vTKLZVhxYxKl>N;+C*?xqRAKT^qf>#4o%LjvF}x zmST`FYGs%Mk;7||8)!n8A@fxTnd}^F=HgHu&F0>XZ*e+d1Pr!kp%9Lr<RtKNM|uSY zN)RZin^Us<JAM1ECWV%zX-fECF>P63vALjZdHXu4lHICSL>00+hdl89(>BrOj)^kJ z2X_crv!=5w*83nAYIfyPHlTqGRMHPnViSP}IK-h!a;*K7E?}A6BSkNeC`u||SzrmB zl!EDlTc)0yQJF5~yOq}##<-1@28m7DtFNpDR0a@>Agpbgn!QZ@^)BV3Jw%{_{s`LX z!?>W6ur+a}?pm)T2S>=pl`)KoMCfLhfnHGQwwE>FLrh^>tp7S+7Tc{uHR1%2f)PJN zQZDG1xV_L(nilGQW`AKdVb_pm>$9%$0`W$BV|%+ip&_HDpUFPFGeFn)wXHdL{&fHM zerSYcE;KI2!1_<yK^b6=_E#!)qL6`p6wz?75y@$$U(7|t;zL`c9PB@sxg}RkNS}JR z@#yuxuM2G41GOU`R_597IR_#c3X=q;rxX|#Z#n1d@9Qwp`Os|ORpD@3vdd2Ha@H5Z zi9VA1rBq2kh{ov`30*E>QER`-8YNz9n|`UFkW|2jI6GGrzNF+aDKqPb(3ZfbulM`Y z_2Wl60KAvILQUfy_==m$`xor9*Lv><mmg9RS%SX3i_JC4E$4Mu01I#eW?uf|lz;ra zy?b6$^QFRIU75z|$8Ek!9+WRN2tNZ8j(76=%-XQ$tohBBb~4xXtLroG>k<_3FP|1- zRTpL+*UnRk94eIK;~I&VMHGdV*PY~wT!lR)Yp~N5`A(k)Zeov`Q~(wtGa{<}*Ze*z z1pR>O*EABwSG@!%7shDDF@MQ}iG-yK@la#@UFK2hXM%bN0{yxCc4J~L^PcPg#1(dN zMJmOVjlzG|RWUbjg$PayW@p=Fuyx+1LFMRXU^$dOMsZiZ76ai-n0pf#v})Z{r0`kV zrvpj0k-xW;^BR9^_a}U{=8gy98(~?w(Gr(lZhuh$xRJ7)q{*e2i}L12f1OZh*?w1Q zJaIlU&LhPjHT`<`r(~5&Vt&n~CZ*+BWTxBAsr-&(9EgNf0Lx+KMcBegWEl+dq8bnN z3yx^}IRs5*yg!}V({&yNWp~h2x93t%SJzitp_7uRb%dJ;6DQa(6zRsN@T(5`Emt6l zCP3N<-1}<F+y04Je>=3j-!uK?tS4iR;JxtHtTg#7j^?v>NP9!CpztRt&7f1zMD0>u zZl!#>h-)%@P1)qJRIz5Z{C4kje&%RUWn7_es6QJS73GuoxF%y6?RR+&xJf8dI-_nf zvzrY`_3fU3jNR1zUil&MP!ZtCq`rtSC0I-iurAP(rwI^QrUfEJCj^#^gg*;phL;~2 z_aFF<9YpKmVUF=5Gwcpd-H$jgn4#SJdhVd1iI)Tf!s53H#xto(j-~fX>;Li-)W4K( z8ZQ6B)c^Bu*I@fxj4$@~=DHFG%aqlQg5Z=t>ZQH46Xk{O(V)QGm|-nNg?TeOM}Z=s zd-bqqY8A9z{)PJc0OrwPv10(k`1jSP)Q3GWC3KY3{h!mlSGrlc<t$&AN|fhYE*-1b zKA3RAAmxo=ztH-={6<q0r+_9J=Wc&w&~={|kx8?&3Yyi<4X+yyU&99JSu7B#8wf`d zm=jlbIZd1jmWrpK;wQpfIRB8gwgx27Y{ohN)^m&$!6tr{DlfMMR9O+ShyqETx2Gs5 zU!*PI3KVe)JzM^DIx7@879aT+14k6cc%?6hpNbz68O-G&L<;sF4q;-~d5|?<zIs8L zoG*%M?+rpHm1O*f84+nlzK)tB$uGonk+3qYq5mJifiJ|7$mKKq8u@8?v{5rxgm7N! z%Uevw-6^$yv)-CmuDSjf>|e9|f*l*J9QNE1+bCL{4#a}gNl-t8Qd=HG^<Xgkwtvux z$H9?|B+Tj#@d$3AU*E(A)s)lNsn+@jr)rIQEBBueGL`W#^ZCW0EPeAJ@O@LOfiKVj zPOf(tLb4${PY}KHv7f9<FwRQY&I!z-IjmpnC`S^CuqXl!g6&tR<A`<>Gmr@2zR-qn z>!Vy%M54D`Hp`{0JHn{qaFgd2PfWQ+JrHwRvk-*k1?!CEYm<x&S}5r!HuFb4L`GFx z#q%}bnzR-x>nBwy!?(-CpD`m#Bvk|D$cKB4RKKKk35WmsN-WzF;VP-ZNtpTiGtSJL z^P-Ehx^<oIa{ujYqT`iBIxAqO2}6hMa3Xs=P7_Qxl93g$j>!YJ2xVL-AWo^+kboZB zJ7&2~Xo}&n_M%2pT46)9RUE#?Ps?Suy66<8oErp-pW?nfw)2}4acSS!J`x5Fj}sX0 zu^HBk5I1?;xZss$WXm(d{(hqumXKubTX9)H<_{!f^eK!e#+bF{2o1A|pYuev)wh`_ zT@Rlq5z{bQ!mXP-&F9esC*&$jlo|w2+uM;#9hG~Kkw}(N0=I$a&>7nfs?><=x{S04 zE64amJ<4xx<}guW@Ad2B*HHzUSZOA9nj1gQplNZ6O2vi*nA|KJST(sHoz~Ez$Qj}Z zI~pLigDph!<*o4-xZg3nM_%~7?JW&PcG}zArbMAeXxpwM6~g3T6&Guo=9hnl&`<6m z{iKqElRRU_$qRQ5)5Eh(otU`C`xeFFu%&g#r}cN4`nVM{iqtHf)#(B-wHBSjHHtEg z9jPxENWvruHjUNuNPAJd6!;mUfM;6!x+R`?D76dXO`V1wHNQ9C!982E=7ci7So6q` zDt3DKIa8OGd)gJMx}K%nC=Y`?pf9I?6<1P`3a|3ZBB+s<X50y7g512IS_30aqlqBp zVUt%2hO3EH#d|s~LC*>pUY_18fT_@tiFqYElTk{<K3q24>Qm!OA^c|(ezLy@m5c6; z?fKR=UcooY>|AOg2S>;rx5*nXYgV`YBWkHBZUH$!?-}DJ4Rz(IXS=2@ge^k*;!pfU zALt`_$!=x`S*l_Bx2!<DQI|rpwlHYR!Cou}feb3kjm@lgcKv4HQc@#UZ!~H3<OZMt z3WW3si`(7to{FvFt=}w;$CHA#s5S8${k79vY&pe4nA}<pyS*bG>u05uz#s{j<Sec@ zy(tBfR<VETl(EE$w1_%V)}ew6hi)UZS(-^;v*<O}IbUcKy!v8Om@+dbnMWY06l1pK zg!I)!rvy36Oo#*htH;ZB*b$^Mx#}HmS|Lev;dtmWhG5F(u;_=&2_iVfVu64^P1|fu zO^a*@si>vQO1qoZ)tw2gprG>wWXVX=_Ed*k1N*dutuNuQm&0@z{DVqJT8^GaDx#9% z0zoSyHDT@}eM+BaeL%Y!KlB7{o1v-{zem^QW>zX{-U+a*8otqPt(uM3@fZZQ-6n>j z+B9#jwr?}!YHNw#(IU;j*LGcrESP}0*D;NM0p4b)oXgjjyg&j+DBnxmW;r4-E`WP+ zEEjvb2F&94*FM?bZvVp)Pj<%Bu-o)I%ceJvGid#Qj~h=J7#$*93cgsPOt{eY`a!;O zJMg1ke++o6OqqV!K-%g3j9(aCcsRS;?tSg^$5aPqL@C$#7O54q?C(ghuvX>_9n=~y z1MzQkzUghgk*gGy7ql3zy&Y<tp$xqJO!Dc@kLmW>rp?iTpqkk|cGmi^pjD{FZxE-m zhq#knu6LsoqCdBjRnRr_BkZrsQ~G?Rkr(~^7zE6_Qn^Vu#=^y7QQS31h@^4u=a<70 z%oTlM^FT}#lc{0S%@r5P-|qfEPYIb74uF@~@_kl{*V8$QRz@Hd({kzblRNJl#Ukz= z(MN(g%Iu2=K3^?_hsPt8SqUeYdn9~PkVG3ZJfPDB<`FC>{@W9CuvHRDVq5zwl|`ur z*AGzepoQ*gG3|wpYV|qF)QbB@j5wAMJ~i<fYvJ4-VjCRCrQDC;P>YV(-|RX;*+!-l zW}m#l^<IRRy(-Om)FCOw6AnQxO~rQz!JA$M^j2NI2%pELb8^5Y@-S8K3+iMY6VHhq zPsKRCPr9si>J)9@Dn)9v&KeeVP<9;CbN~v2;8U3}{c?q0%-<Tff@QL?UDMcQ2&}2s zUoUS+&(4mD-opm!g}O8{jX%CfJ=8&~{>Y{mK|qEwpBerlm0C0NL0ic;LPAyQ7@=-B zTm<vDo6&KdGxH%ycmHo&_G;n8&#s9`q~csphlK|GF9MxWj`d~z=*D7AL!ZY1_&bK< z?&2uV4;svVT-R$_azSYg2Ul%jS~oO^@s;DTpAA1fuN(YO%aN;DPbeUVT^%t8a9!g# zx@ULbq;*5o5vY)px?xzY=;;NuFCLN;m`@-nrUy$SWfJ$4&1M>ipa1SAR>M@#EB29D zqnOWh%6d*Ig~d9_=!#GG;<_3F%Afqi(!U6K7BgI{JN*QM$Vo^ayR4pslQ=|<_2nxB zoMa@$NyakSaL96hMI;#h`gXjT3YU1k=MFu@4xJfDjwPDGb}U*vI?QJr&6CU`{6hJz zgKRssriJ)P@jH8M_B2eNRR1KocpAU5vti3z!qp#%N_%+z>s{yeS&<MRjLyT!Yf6ln z(Ns&X=-2JDsOO<#N+7flT7TEPs=5yp;hTKODT(V>qJ@6DrM^hxd1c*(Z$PGWxczm{ zK?+@wV1F%Z)X^>|Pvp{Kb7Qxq*t+<}QXrAa>!=yXJ9-`$$Q#bSy73IW{kyxX<Jti{ z9+s3oT?pv0&bw(OI2^wI^u(3jY`_2ccp7!|EuZwqq}N7iEO592ORGcrbz4V#7aez0 zJoAKqD6@c%eYw|HvZ|UOmtF%N4C1W(@_q%ehXhm5pvLnj`GX;3p&=}L%wRlOUwTvc zy9<uBo%u2)4Lr%u4)fRZ&0p*sj1XWUOwUTQ39t2OHpHdQy2x}u-AnjHqk!{|T3iR= zDOh+6T{L8wdov`DnI^w)g)FmcYymHbWh6KhwTI;9DAhR=L#*po554+-H;5xT;sc4L zlZeh+6%Qv(9)=F3ta+_)Kc3I7|7MSduY~h`I=nAR&_R>t=u+GEJJARD38tXNU{lLP z1q#a~5i4`qj|~q4kLLOb5P{>|qJJL;$v|CX1jQvy0;@xZ437H4^@%n?WLUpJNGBn$ ziDB|=tOzqoM}Q>k2>8A2wS^=7vQfhU*y?D4>$#T;4{D?K#TaQEoXByYgUDo}%kP7{ z?5*@B<d~$RnehSNH|T+Oavsv&xdH8>9RrDRv_RK!6wPG-&jg+3qJl&yCUs0M-6}CB zy-aD=Q^7<A^nNo>L}iJ;!8d$~IImYrJSrhvi7t4djZ2J~uQ5OD`O4fnD{6{+$aCtY ze+6}NS;1Xg!^-V8rlEgrCRiPoNP5XW<_<IE81c6JEfn7zMIh!b!<opAAl5BhLd%_8 zYr1s}rCkdkH>K>PuuUkIVjiLU3LTv^!nL@wL~UA^n7jO;)%u)yvW+A{>tWAJD^NCc z5+7={=)2B|Fr*IoHbJ+9R-Q2#ZwRw)Q#p}e!~&?qxv_Vv%qRnknW6QV2W}WRL@xaf z;3VU|2|9AcPsj(6et!17>@8z0;Rz$2%c>M3Xe0o<DZ@zgQpfBLj!aO<(qBsABm~$I z&*8MJaLESL121Hj5k1vdU*!LY=*R`FG+|@^_;i-FG%7KK2x%ybVa00=vVH<(VCrVX zU0Mp}Z0~}oPV}8=&GiEnnM`{9zkLYVB&L4sx>Z$W#<6r|YU`L$sAxShf00Jj7Sf#f z8(<<^dfG9Wi)3kVyaPOW0J~0_hFGE1Y_GC**z*VaQ#&Ve(PB|fLJM|X)#jJ9#-6m| zFe%8ziQN}VD{$)$A8|KgJBn;Qn0*Lfj_Ag$Xyo&wa6f~>(xJv(WMR^TV<SzboG@m4 z@oTT}kLW2=3*<*2IoDLOY}e97M1Ix00J?YcgJ<EYqB;f%2S%iG#1~01cj6O=sg*2- zS`7CjSY#{y+Up4XPT+Acd*9kgi+B51q0RU$-*$s)n0*>6jJK{o8_b{A;@Gt?xT1Ca zpoxkWFG!-#;Ddq>`nhyu^O~xmxTly}(S<4Zl3H2wiZ>LD!Y(J$Nk$hA&NjM$(eSS4 zPd7LTjXhH)94<p8WD=iqRrvkRs1&3Q*$zRlDf?-@3h@Yeo;S`AoKcL(@%SYW57pt6 z(6W&jv!$yCtAZg+D8ecK-3qx{-)&#%LYNBJ@R9K-a<W@FvTEs69cMZh@|_bimBJ93 z_(oDXRIwlookV#!E%P*0ki<R!RMXt6#v_9^Znjn$bHa0ga%twORNWxafXF9d{$Ma^ z{XjmVdJLbQTAby!PjBB2!dxP$-MGAVthPNMg_uaX#Ho?=<c4)iF%6x+TuPH1u74he zPSs$!k&bnFTF^(LQT4E@|EhFlPjNm-?GNNqk0X^F8vhjc<%vAZ76BbV=&#cyjX7zP z7mw{#8-;#4veuLz<q<$tMu*Oy(ciZ=D7pI?BW1QVoy*9YOXqQ+_Gf9g-6BIk?u7?s zpr;Im#xw2Kl`{xqvk8r6aF(m3+qK%u@8dGDN+c^!?(vdSh)NWKa5$d=TI|o*r3fk# zFv9Fp`PN-k{>-fLsA&fX?=ie}tDkDBI>{x&pm?%kXf=)2KTJ%5*g?FQS4rbAR=|aJ zJTALoMXKKu%ECH8B8hv5NAv}&XP+jniIum0vW2Y`RrAqHrg?qH(po)vKeb1qJQ4Ee zkG?UXklvch*?cA5X-5RIpe&^rbF*^ll2e3R!;Sv!`lUdK_yKU4dD7DFN;1c&uwWE1 zMaUZT4Ij()0x_B~ia&Krvk7{~j?AKU{F3B3Y&StoYidT1?VQBclI%8bJ9IczKC`Fn zpS^`v>@^A2*|M{pgRQ@wOvu>&fRCVf{1jn#U(<k=OcV{b&Us~qfILOh<7MCmc(EeK zOfKYbwWDh|FFY_AGq^n%^7e{1YPWSQ&{C|a)p8k_GC5;!zulCt?G^yt%S7zW?Gy1E zu6Jr_d13ouZIY>M;o@rRVr%0Q`hVOFcFB4UgT%-ZZ9&??_7AxDJZXjg9w-jcSLKQk z%o=SwnbHoxbvz`#b30qcUfM*G;X>=xIT^46T<C0Q#t-f^(pn2%$VuG|<r#7X45#T_ z62r&oz+@ycUy?hcx+kGn79=0?-)BIYglHe$duONyDTfG>S?PdhwoKvA+>rN<zII>2 z*K?jLrNb;d&yQhZZFyNj!5ztxFzYu3@(waI*V9gNLfhxs6Rv}Ck?yY6Y-*esJC<Cj z-`1HBT*g}%lQ>_x{669l4oHO}nH8A+yub+`Z7y!BpkH~dZDoIuvoVscaxqvo(|<VV zIB)TuZ8_dx(OdYF88ua{={ob6aqDX9@A7wjCFT4D{y&BzxNb`h_6|4@kS3R8Mt*$Y z!V!-P_0t#hO$hm)3r~InIhCSXP7UcD-N;g!O&8WCRp5FFIce8qkHJJ_)awD?W;(TC zu>ntG4tzAJ{fn*(H~!3<Wf<?jM_cTCpQPdxiP4&C|FTf&LzX<rg@a`xkCVFX7Uc=& z!!HZdVS5X)^~K7^c61T@5zIfyGiO_1#B0;GP0dkK^yhU`hPX1DiA)5~8%JjA0yoL3 zl2ynp^LL^jyp9_rr7G}Vc7)C^*CHqRp~ooiH$Q(KN<fGAHvf~Nxgxp~yel&w!eZgE zdH7J#LqEo#@+h-W`C0FCo-EFQm0U!KX!w_%f_o{9w*>a?CG!-|OZUoxog}gxXn)i$ zTtYlKpxZafEj^d=L;aoL;>Y>SUEqnIpb{cx&mA9164}nFt4HtwEK7{Ap|YS2@6YOA z{Yc-X{>E%Iel<<eX(`x&sfYDjhy}V<IXj*P{`;Q}4@~C?EE{`>tyn#cd7~?*E&n>q zX|)_<XHl`A^peYWYWpm^^KJ@Y06%5mFNuou->c2@{o$av;y)qnDr?kgH+)QX#i-A7 z(D^+ooULVsOhkQmWoz&H!hcSj+kI+_;a<>mjP~ol3iE&?QmI*SOp=(u=BUP!)jHv$ z5#8ww953K>;`B9y6`{Jm>=>3U@OW-`RVDhpagd<3up5Ze>xTTxoZ-sT0XPRIu86|H z-Kt%blt+3~iX#*f@;S5N?gALexN9LoHYStT*1{)&@^>W}(HoPN<dD!Y&VnRcS<yEE z3G|s}C6h9h7?GsgMp+|U#$HK6_+t}uOym;!>?G+8?H+ni99nZXrq~8(R{Ri_`Z)pX z$4>@s@V^jg`13HD9EPW0fH9FDm~{|om~gHSo46V~D9+Vq$ZmdNM4wB5{@+UD0+X3< zknop9lQP>Jl7w-;GY)f5tYGMh$3_m=SPEcsq46zZy5e0>`)P-I#a_tX)ztf2=B!&@ zrG%-|^38b#9dZVR`kZu}T@bsH+kB7)IFS{oxQqKqnfQ?*uk85Y0k>;o+Om7~q4}0^ zW$fc@rE(+K2+;(!8~mBG?rxs|m?aMC*5{6Ea{TI@-Ntf=pj~Xg@I0H~IoH)aRpRmI ziq4+vKmK`T&+5w@TbYn|@5Tjh^fQ+dZZ{Lz6Lb{{p{RiZxpBXg1=Z?aV*kx&uQ(#u zfKpYSACUvLlU46Jh)#3NCX)g+RQI0}m`2*kGeS6Af}azBXr_sB3KaX1mvu!~TuK&^ z+74JyXHUYHuVOJK4Ks!h{k?1xX1Lb9>#n$~A!xk4$8$cJP#7C)9psx;IjN#!tpWkO z?cj1<`EtKB%PDcPf-oV_gSE>)X6y_58%K{BJpfJ=q`Gt9pRFnHpFOctbzg#j*C`|w z&^0{cV*B3jt-qfQ=`#z&4}Ey*!?!imwMAT}z<IpEH`{O;^rUVEiYuwO33`a+Z&u=Z zLDRM~`DvO1RkOISF%<(w3tJDaa#Z!O11QY!q^E};x6!aSuSoF$w&{|pGYv$l*IH-R zRcojQylDb*L<YX~SdT_tld3c&OKEa7{5kX(*x*Q3Ty2}Hw2|LyXmUVL@y-P6y_La| zu6Ia@6Ql`7&(w50p?6g%XUZH%D`7AD^_}YhVYqHQ?Vkz8TQ2#x9M00%Z(OQ1EU=w9 zrR2Y@(?W{_xM1yo)#tT5UGt1ZQCEBu!PlUcml6|0{b8<x^g*R{d3fm1%~MI+A9BRs zkp}#VHQT^zmPWl`=7<L8VbX8`e4E%h;-u&Tu)2gS!*8`pj|HKN9VEg;n5Y`(t4C2? zc<w*AhFAyJ5?I*O99&}DdBb&yb#SP`@1%ZuqyJJ=z4faAM)MIa7wbyC4Txb$<MDQ@ ztgg}I?X)E?&&;&3+z|)-h8SM2l1!_FqyE|G+$}K_;KGdm<F#*BNM=bjKP}jex=5d< z>bpS&+V|NKH##h=YcRFc-N;7YBlxL@siVDOS`Y3`GEy-*yh_mzmy#)b0yfvNVK~<@ z4OZ7`UlG$dVCkO1^#_3~KdEcAUHJrlD}w=pQSS57{ZX2`{7Alk1@|;J5)&|#aUI>y zItwCC7hfEDo&T>`pZ%G0@82wGY264S*6rgiViKd>FK@kdl+{i$xtulUx3+mfI33|` zuYhD@(Brwz+r{`FyumSX(fy8xhO_CoxJ{9Y+NAR*K!ng#HJJv`GdP}dOP45k-<8Xj zwzAC<2QGF6ed(7gYN>L#@sCMwbp-i$VhEZ3De(YQQ7w!1@)hT>UnjI~Jk91VElnA{ z`h4k6asW)mUo=SgXDJ%yNCGw<hEA$t(&x^XL4#o1#Ad6hy{c}4?#`FcqTL(KY;13o zZy=rtfZ})xeTG*)PGzX`HA|Ylm<jZ5t34D)1K5{0DK*5KAG-!>+cd%M&1&fAeQo`C zI_`Vfe#X)M?&=28R7yGO{|wl-U#bZ|Ked1&Sk$C|$R{uFd%1a6c7D5o_ZwYrG6%B- zdy!-eXro4FT+NrVGH>gtbrOrRBDK4QZh7Me#(S)Ai?)*LaA6SYT;$6?*au$GoxWdb zKr@SXdzt$7gEsE_kaD{t%o;CxWbjCJrx2P+g?}^Y>CRK(pM~Vm@vVxN`5upHhl3A# zz^_=(;d`11sYe&lgMwN`f(!Sf88rb<T#O=AQr>5cv<tIZX|@>a@ZY7Pz-^Ts=(Up> zP}SH?C1`>eQI&;`#(bB*emt*LR@B-ay5iq=<Zb1dUS`KjE2v#$UH9D^br%&=&rtr{ z^H{PieLW>ySH;G=??m4AiO6$#pS2L5Fisg7L*s;fp00My99z}M6-M;0J-Y3%*S3Y4 z){brPcH^S#6n<-<qW}VI@mU1L?XmO_fX15(PsD4rUniI#41k~J;nI>c6J9XbfoO$H zQHv$2$9J&6NnI;XQZadyV}z4?{1yx2tS-<rPIwFT4iupiuo00xQmH7f8k7wy`QBr= z5?I%4*ixSVSSbb9v*G@SdDqSjW_~M<bW3-trcS}??h=Yc;KeMXr;o3W7y+%0axbVd z);moI1tfsOuW_UZGJKldx&gCY$f+JBaVG5-s+RhztRYcyxC9}P6#|Nd@UUx)jaCBx zHe|7ApIX$(Vx~?^6jyP?we6m^O_98AF@MF`CKn~^QtZ#J+7&ZQknD|<f7bqQPI@b% ziXgxqsvdr+R-FTmZpPZJYwjG~h6}FG4c~5W_wBAVDfru2!VK7(`x&w<b!T$_#xYlB z(FeivwYpzIk|QA?M#Y%8HF`Is$$j7C`6~P5rKzK!U|PMr?IFZ(LFcid`J7u+bBBfU zWOz6e2HmrK{Cy*$H5S2AYD2Qw-h7&8y|&06MQ^Y8ij&D@y_<lNpkQt?BhCu%wmP{P z=@T?cR7|<Jzb&~*$Z*wCWWwTY71s<DY@kV*?Qe%aO@M#~lfOmONjb_*1yubX+dp&u z6=HFCC=b}6hWoRRAXkk!n<JRqHl8%P#i({IQ-ig*%bi}C;ag!U_xf+8#Ba6R*p1ZO zwwPGt&ltUpt(RM2L^+qAkpC$^VNGYx@-v7)KxUGABuM~0$8~1((H*KQK5>7l$Yge8 zaa|Thn-c5FWrN%=)B6(ljGbEk!mro3oRCJhtb9}_{Lzn`2O%s<E*}*yC3$Pc3w#qH zal)z!de5GjxfQ0#ei*OygihkrYMnSl`J(KL=Nn4C3c|-QmIKg&WuA5Hg_vOBg>Pu$ zuZ<=g-88^|T<=A?(48tV=+{Z*LDUEf!pMe2Y0R1@%;q{S6URvokW~usD%GS=`zv_c zr@54;JlrCS2dy~DG`Pamp2O4qB8gO|FAfHB5}FvN+Xo@aj&-|e{an{u?eAK-5M{D9 zI=Dn*B6ttBe{~WdLkMN+9{=0)j`n4G7m)xQ;bDykbmrjc{mJMB2JAW)Jtw2_-|^!H z*7%1eMYspnj5!sO`oo=a5mCD%Ze0=?N*{)qehAQTtFKnxqtm&v4<Bq$;}`CF@AIDG zS@HQbKIN96Bc-gGnjqNw`hJ+})t|fDZ`2n1Hm}?e|3v2JD7M_*D+t^lq&dV{^EC%b zG8^C=6vCN7LvTNauJ`ogH0QBtE<w3)pju@q@^r35%`k$Skefq4yxK#X7bVjAu80&f z4T39osV{zP$?13UbZTu!edkPG@-WQ!CJrz9Wp=l>5S-j%IJk&iGJdefbJxc|mRHL_ zS3Z|o{+5ctytjh3smRaHyT;dLn<xeV=u##zMRZj+zmL(pepHFL-?WmoAbiId1=(P6 zx;$WqU3kDjZZHeD!ftpy;TaVwp5KDpc<t4PD0f4y_XCL}GRlZ-4i2~?>&Gf{T@~qb zPoVDNMwQM@25$}ia`Su`pp~xz=UI<Ei6ceHn_-{I=$(k}J&$<*cYoMqBC|yTspzes zAm1cr%LWRR4$>4{^^Fq_1sY4-F_5?E)yv4CUj4gK*r?HLg(gaNy-s&!l+l9LfZQ>z zu67HPMlS+kuM1u5Lg>NFzD0ywW?|6QupLu|#;m!&s7m2VS%Jz`*a~n)wcC|hSKT)M zexonO(&~#8wKm4rj5WDVef&0e=`{F4wE?QQ?->bTa8qWOwL^zZi;GJY@1iySWnBV6 z#utNxYyR3CL40}>U|TRDB1}wG6kp)(oMC&+VEUp{J@-$HueRDRGKOIb3iaP?l%*Hg zP`mZgAyO+rmTiK#wv5B;q{(~qo7G!t3_r=2c~U-CRcE;A^**Wwwcju*{_J|g6DzqR zK@Q|Wk{OkyI<jx@r{<(q+7AgKhdr=JMDEm9b$Oi&e_`7RCuCY=nE)a@B=CLkdot0K z0{Dlmh$ucmlk4nP|DU5_2@Z;&JIOl^yr7}UxQ>)~|9ij%1O)r**uno^5XpLubol?f zDFp#R|KG;1Cj)`xLPtvc|B;R9|9=2!>EuI4aWKs9$+S-VU|TlH-<?SRo6_WXCk(I& QyW~<QGAISd|BU*70F%D;tN;K2 delta 13668 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 C$kz`5 From 1ee7d743e06e816453aa19487142bb44e5ccaa21 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 7 Dec 2021 18:12:20 -0700 Subject: [PATCH 025/112] gobp-pathway, pathway-gocc/gomf --- resources/pheknowlator_source_metadata.xlsx | Bin 29287 -> 30970 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 033e64d22f7095c175c673d897323808fc3e25d2..66dc5240dbefd1699cbddfba1dde6a5efd5803e5 100644 GIT binary patch delta 25142 zcmYJaWmFzb(>02_2X}V}5Zv7zf=h6B*TI78g*yay_u%dj9D;jrcR1Y7_pWvRj8@ll z@7-0^GgEcA2mX8oUP}WH{Sz?^JtL73o)J|+YTKqI!&SX0{l3n*2Q$$Eo*HP2otD=x zu$w|$6r8%dsF=8Pt4b|olAhG%T{5(+0ooqlT5gbWlx(JQ*}*Jd+6-h?e!|^(Nf7+W zy^3wFRdb$2gIBu>e^yjhy~Qf3FH11XAhw3Hxg!##RqWj|8+FdO^tH5P@E{FZ6o+{0 zGS(N$=Xj!XL`GK<v)KE63<elaT2mWJD(l-^UtZVk2UXg{cMeuJV_hEh3UM0M7IXD3 z&Q{>3Aa2~K=76)WCJ6qCQwX^V!?cZaal{7&L;0DPhr(*~y-+e-1nhSP8r`JMvS``d z%EFDlP-ggSnIGdEQdJzUPqpJ8w>uQ7B$vBYQFqECQd_YuS+MLAJOf4|Nc!xx`0lK) zghcv;r@l;$K`ExC`v}qey<&NX&?yz<y|__2s<Rj}CL6Ms+XhQD-u45r3@9i|_D6=0 zUoi0zU!ir3D7~Z?-#8MIIm5lH3oZo0r9jD4R))^1ONHc2vQQU`zESUJg}4E`b_Pxc z1t;_(a`jzd%|Csfn}GdHssue@Ngu)Rtox(vf>EcJS6HP)Jt_o|Vs~rz2O$e&@d(H3 zvnEXZzlPLurRz*yp|U}Sk$we+<*F;FcFdx$m+j$aV7dn8j6o8%+-1ACD%h|vNwjXY z=|ee(uVAcwh%SmS+Gh&C@($Y}W4Tw`$=9r4Gq-ypx$tWa-T{l!6Q%yf2Qhe!;ccRV z%=bw}>vFpbZQaWl(2tkAe`Xnc#1ovm2LcSH97|{E4n&LZg{O58Ln&t!!+Ca?4Q=oL zbe*t=5vMPM2H1ZO1e);5K~Old#;%X#Ncgj}iC)mgz<yOGsL|o$L}Vh~=3g2rVjXU$ z7QI7o(*8kp><2iGN1^sQLJ$X4)osl&^hEKngW97@1v#sPC1$Gh?wx-Jh2cr}#<r!U z2b!dno#DrlXoX%Zr=asiB)2!P%TQ=8sQQ`qee9MJ>C$C&8AO^%gy_M8gnGFyLQ~L> zdwmPMp?o(JWAudB?DCFGxxWz-rb%LY#corlpG`Spl&IO?OKOaM74n*Svh>Zs;1LGO z-$@Sh@f+U$8P)w|I<<MuSL)9c-}n2t?S|e6dd|qf&05dK#RWQa^s{k0<TX(rRxAqv z^^S^Ad@0oYvQQiVG7-%Z6R6|ZDjkOc1M8*%149M_1M{@~>f-8UYv#i2X=nRaXWe0k z8x0UTeh;I)U?-uZ6AAeqNM6<G!CNM9Y@o0c(4-hmIEU=h{^2+L6flzlK}Rxr1y;8^ z@yh=#2aoOBBS=!+tCydQ@m_sbQ4@>sg^~2?uiA%yj=Og`2o4iA9H__j`G-&QFLnE( z8yTan_|Yn`PB}n4%k!UTxS8P`1`tS*^tX;Gc3W}8GXx6hNlz|?uz)lHRYLun+xjfI zxG_h`Ox&wUxOg~V$iycYl_DLFmoHxVO{W~=>8<7_&56ALjDv%5kD+YaQBB5clrqz= zpN3q}0v>UYXl6kR7r-Ft{&`|u9s0^dm8Ta6GO}rJYj{XnDm1ks>_#gSbmNB8X2ck@ zsL=8(hZHV?d)_p;r^p%8&D%C!aEyOdH$eM|r?wCZnX58{1zrw0iswu%i9V$njZXDV zanCfITy*Kl^<J;|%vfJs_})30+G)=+G#F3b*7MlG)9jnB0f3rC`pjHH^os<|7yA7R z)RXT)ncCyg<qFnlg8FDqCy;XuUY0{2BHs#M8m-*UN~*hxAddv=_y_9X3KK@w>Fu{c zP|?5^rgMzN2})&-OCzWJ%Ofm?qztLb#PIi*2}TEvi3IK*I+UJ`s%S^(ipBvG$1|hE z<JW6jr0?kB#DLaYwN0D-G%L|?+tNkHeJ6v5D?*TMciu4GE;#qyA1rBw1GRqe!F;gt ztO=ZJ&Dx^9-Vm^1E<z}%N$u!T{=xk(3p8Z70h<r%bzt54?J-OA@r(#4QdQHnrpmE8 zDDiRwpT}HF?Vale1juaB$Vo~zzgcX!DFusfWioDrfbE0Ak;k-9+24`H36BT{&=}-+ zlVfs@G=paT^w)v>Oy8|}&JeWaY9use`jLbEO69>GiRm2~_U90En2&YL!PuG)3sers zns;Gk51c}7sQSS}UVTd$acP9MRni$A9ZAIKq2UWi{oM{=rN%9?%#jM&|2TFN)<kHv z|N6EA4IJrqlEl|Wrzj*oBwyufKRaT_rM!Ecy+9?NH~$r{j{YE|hJdYe4b&Jf>NeS7 zc^vu4)nm3K@kkvoAA^U3HOnM&#)HEYUZoJ^xe@~X%W7Pe(ea!~3<;(4Df~mzY8$-5 z-dD$`XNVx~BVdpV9awYO(0Hv|2KQQ+QRDh$2ym><LwhZNxj>8*4<xn6JK#$G6YVnE zD@A+??fv!hLY(<_sW5`<DJk9^=GDS~t|d{K@G-7Cstf$9gif09g_a!Rmm>z<7ljkj z)LTv**<aP?qz%@D^PBV%UD+6$#abmJi(i!9)ZFQ2$0|ZsB9%+Wy45xyGep{TSwfsv z&H&KEEan^0`Eu7ZpVZhv^3poy+Q0W=sy(N@*auEFOFV`rWR{RqmobF2%+6jOILB}e zb$$nX?|=8tsD$y~p-{@T?r^+}$SJnV)=nJixq2Ka5onM7<nq!`yT>9wpZwtDdZ9>M zVqQ`b=S@@*brx$AM@_VbA4i>7R+0JqXwxm*vOwcqRe!)zN`bE-ls}U&7&VE#mD}u+ zql341`3(=^l-_;G^%iz*hHtLg-jxydj#r?>#-q5|NRiOy)>7#V!M(#=DWw`ua$}vU zcg;_PTwX@+U-`VU8+H;@KPqQ_a>h(^wnx7q*nj>mG1U>Rp#a?h7V#k1j*^-d42%#G zJkdxRH<1Gm9nk8ciAY2f6xUHf(=D<pU-Z)>7zVF!%33KFE_r((;)Aza&=;oK8W{3_ z?K&j1aQBPH^4?p8`+k@Pg)aHqyd3S0dRVBd2F4^hy2wq0;rk6CNn7udCW+9*y{u3g z(;+r^J}1wH&YFDxOf6IDJXHBM$EzAWQ`yh_jgaEmECAa-v9galvR+wLWjBRo;gCgL z8(}Cl@{F>^4srJPFYMdQDoc5##8bko!e^r;q%;sR>EbVp`=fkZ8P3l2dD%HP=q^;s zA|xVqU*kvWH&*}mNf}j^s%UyUu)W%a*Tr|rtdOK^X@|GmpJZ00PX&(9zLJ9%iQ=I& z8m~xl8vy*cB1<Z>z<LDvFz_pL&{9w~1+rdxM}%D_vDdSUJlG7oDC#>Wu|+{<_oKB2 zTxqS#Pxh}a{4{e)PU4Q995+O_YL}f}SJ906n>Uf)#<wwpW)oGfb5u0>Mf0kZc5AAs zD(n}Vu5`*^!#aM(plAJ^$tc+MgPh!w(cm!jv;uH`2kE=8LE)Nd@L%(3rR_G=Ysj?p z8Db%-6=^8_zSrrbJnU03{Ay3e<c=j3?{u@~a|g+SpOe+)d#+#ey3jsnseo))`Y)CC z6dNk|cTlRU=8A*$M|#xc;))}cqX0isteicr9x)5SfFgw&7Rxq{%(el<%**Z8ZkL#1 zkQ0C*1s@SMwKKc#8ic-v$Ff1PmY@UUd4&`%K+%>29w`86JRB~S%JwG~ylx2{`Y<EF z%>Zn`-bFNsQLf<hl;4yH!Z@y#JGJ2RzDZ$!fF32J!H%6wgXt?h)(2h13Ew9A$7hKB z_<x&1{WW{N7SJ1(n36?6u=uGD0T2N$XXgPS&+KA6@goO`s4W}ri<|}JO5*FAW1>Vo zbJSnkgF%+k^3}f;G8#<STql_&n~Eie{gXG76%NEC?-zr+EjZ^pgLB>X=ee&R2KHYK zUhiMe295o$cJ6QHZu(0u6g|2XnGB9lTiV<Fp0=m^kB{$NFV>j5b@YBe9{}$4SKe&b zXM^WEKOKPUDnkQ(|C_s1%}rlFISfq=V=}7hssU>%2^0-e2C5CMi0mX!8>$q=NW!WS zK{?+uqw!@XMt6G-$c4gP@H=C?ilV+q@wAYC+ipqAEbz!!wfX)aF44WG*$-P1!_JT0 zhqwnr{g2z-yAzMkWy<T@ATan6)!p4{@Vdrj2xR#$o_K^kgmnzho!w-9^&nAvXrY1r z>RJ5^9jh|%kqN$x1qIbL4(>~n9dNN^b)+H(IYryMtyhtdYD-z*5HbsA;F-H=Ppm}3 zY`~u*=<jo7{&XFaE34$;U#~}NnF_y36?Xsf5?3+|zhG!jLd?;)1qiS=s)!5g7N-X+ z#t8|WS%jSzQsft<z%;4^iFK&VKoD5BcB7MHpTgCCSnWK%K7Gb7ZokL(%fe7<&-kIQ zk#MiFhEV=<7v-r6-R~TlqcT!EIJ(@t-o?5R**X7A@5$pP=h0yH81=j+h?E{m4BuaG zr^7a}9QzEk*0W)lfk?QmF=vW^o3B3iJ(;eQj`Mvm8=`K<tC2?rBp_)?l(g@}2US?w z^h)m0<b=^?+DcHk8{0X^LxmK*dDtkDPFlSphPvpGk{F{uw6cqw$s7X1uZvWSS&<d5 z4*aXL-VPFD0T*KP)}m*>>R<7Oa19=_YGblTc+j?%hjik3z|G$xEdQl5{$7_m@hEbB z?^2%WH?Y1$MPY8f>E%B`bF!9)>6F(a81x*6ZNSrzLPFX@j-t=BU!VKtnLuxprENiY zPxFqz`aW2zh|DiJ1t!nH@cX=xNcVhI7(QsO$v@4g5^;Z6K+!#QI}-h<m4A<HaWRrG zV_8idb}S{L0UE0D@f}$gx&BnP<paMm_V8@FQ8+M$jrkbm_`E`}Xb~RR8E$peFQgTl z@9}2Wdayc(XKD%iMb1W27(1n{W?o&F%=RHXP*l)zNTtw>t4$ysf)vw|9%Ckgi%3Qr zz=JYB`Koq~pLcB#hN>H~Cm5$>CzAog`e&8!K)jb6fSh$-|3`jOtu@&LOD%6(|L106 zJmF#$sK&9vUMhNHhS8<&<Grnce-XTyrF%j}p2)ZaZBQSYZ_|K1uq8yKW7oC_Nishq zTkJ^E6-6N>^4T&kYWmB}0{<DDp(AFX%aO6#7hVQqex_Pu{;K$N=!PMUV<Pb|<k%|} zh~bP9fG8i|NcHCe>2UD;`~d6FtKNY3Dy7;`-poIe;O~o(-qv8zs90c&pXyA~TO(0R z-<);8q7?{<POPe;^KDGM`;D=Fta$1$m>e-^ALPwHRuGWkiO2+Eh&g~qUNvY81%XV* zYU_0try<5O=G01Bh9;RMONEvHsJj<EOvsc2SPKy74PW@(F4qu^25q4c(=&4Q^}2i; z(sr;fEK*menVE0QugpH|S&uSTm}A)ll03jccsB&s3Am3cxrdgu1*B>#2&BK>Y_Ug! zRp_@lnB(J6grfdKp=Pn)vLyWC)RL8UG(0^c!(`DJ`OG2#>&&8Pf-#LlD2mw3F&Yp! zH)=HU&l<N32KCKeLncDilMhXU%!Vvo<br2^v1;8l{2?`Oq@3%w#Gz=UqNM{f@!T+N zEL<$K*Djtg{-OP%ZMwpugYnO}QBCpw^+|^B!PhEEK|zurrzk~8;kH3Oa8QRc2U~m5 z{z6(bWtLg8RahC}W&dYM)x4@-atNS9w}fl+g2ge{*_n|LW?LLU-slK&OC&m!JU182 zC3djSy&yh!i(POZq%<vMAg5515oMGt6;TFRh0o7J%u*xW(_{0HeltJAaWB6bTO4Lk zvpw8RPL9xQ#=EzJzFz|O&6X5@`-VbMB(S_8tWCa$7ldw5m!~eBM&aZ@$^kUZ&@fRO z6V_lk!T6E|6FD4d`7q$QEpJJ`MqJdXH^26w`Zezsg}@FcWax0~BxHlLjV2WdTTb@f z&+XQ5J$NYPb_pG=pz>NeMzI;+YfjZNN<GRcgBpZ5npEqBhm>0x69dIkn!2IcZ2T(2 zOu1=K#;WI>L!>_q2Ei#x1F+^Sy8<uiv`eAO(;6RIgBV}EA!;-8x;GC&V>HQQRBL3o z6VppjkD>rW-?eLy2bW+$JhDrDlAlv0w0?f?I<Ql5xbvmJL*LZ+edbKbfu3aQE2p4< z#w5bBft@~!*p*b~wN@l_)|h21d||hA(5l9BQHa8Cwp`aD@%HU#7<j-u()?^0q7Oy& zvuhtLBPRj-Bd{?MjC^QK7VngE@g_p|mNwYLmoCs`a4nSNS<ivs(|e?_;}TYdUp1Si z&;I@UC$9~uxx6LWouz{cFHm=KEmk>=YVaj`NJTAi9RlyfuD}XK&zdg3Vsr^&d~r>Z zk1(mcg@}w)?iUy#khDC%*Zeu1UbTz4v29kobs2UWPxzeY{;bpEvz#q^pYE`pwxGu4 z+JEjH@z*}`y=ENK=TP>g{vNHHVOX0v2ZZ1C(G@87VDP}4{=C^PE#f9xxa_;3OQ3Mm zG<C5y(4t~~$N!6I*T6W9QHVK@!iHA2ftSX*p*^DB*&qZcHvQUFC)mxt@hUIkRpvO% z7Ws(Qj9}Z*Eb}}}-XRB$HQ|R$Ga@o{#E8SwAjetB$SRLWBr<vp_zKU>V;O^`tbC7E zAT}eHP#^=m&bmV#OiB4$@J?H%X+8;?Y>+olEnH;G`tD%QNKz(|r+Kx`Fc|)h@10N% zcWbQn?HfS&oQJk($~unu7+^M;7ihLSClea{{pOfK!sQ)bi?s*m$6&Gf5cjPSN~LK@ zfEg2kAu6NIhxY{AM*ZupaY%5bv2Ar))4Owcp-v%MIBsr^83Jw4g>Y`y7;KfsPTu$R zl7fwl1!PB1q>^Dwp>z;vk8*w<YStNJw<7^>ml_Dk&bTnB>b%JLB{yB!vg%Q@Xhq`F zBxplIa5tSt^0L%lsYx+GO-pyn^f411->5TzAC7CzWLB#Ne<)z?%YIbbI}6I!pOlJ= z&u8Jqj17n_g-dGWj$Oo|`DR9k7HVNs@nUTEB>i^0JzJ4bDWj}PE1@&HH~YzWw03b1 z6u?N3YhzY99m6KT?_DIVfTM{di;#p{$K}Od)+8e1jzGIue^;e3RFzb~cQ9;o*iI~C z0Y#Nev2mn-LOCyu1dhI|+aM1kVJ))RJ{4B6xa0VJz2(=&zwZ&I-p&Ij2_e$TAozs; zJcT)Dw1b3sqeC(q$7Z|xJHa~L&P|saP~vy8O7(fc;z?s(C|K}{CTFeka_3H(`M)vK z3CxLZMq0jSBDpZY_!`0RMbx@1-B%r{z(^UZ67lR@isg3L5PjN10{LYFDX*lA&Tmba z5z}Hu=)Oxv+wYqQIrc&;RH}nMT1H(b=tG}z*IwLMhi9j7Q8vsx9fGj766j>Awf)UB zdb=0CMBz$7OQ(DkufZUdHJ_13$p?*Tv*M}yL))jzB!zX#o$|o&&=%%J+@q+QmAnia z9+G@<E#ozGzHfswcSOe{XGFA<;{<aH42!yhcpFSa2FCe6rvE^l*nuarAig6pgyR@j zzC~2tVQzXkba4Hz6_D#ej@Vf&B6MF9Hd-y!Dxs`;w05-N3D01h1yyU@gTuCLcY5eD zaEnrv_AbpP@V_yi*&VxnkvoCrJDYnH9$;BvR9lqlWj8mf@X_tjns|(26Zq@?P1Gm| zL<r1(TDS1f2I%wa>_`vU_(2ZRi&faC-zW)NvzLUERt8k_mZy7gp5-W1ZJgH_)r9L( zVp=3pn*MVE*Z-2x@oPsTU=HlRy0N7d4!A$-Fi9*4kLc5z`;Ul1<0kl(qSxhdt>68q zmR;SW(9QHUJt3h1*q*oNj{Vw0#|RU}vlkLUrk;ZR`Wn91<_M|N`jbJR2@%yfq(7{j zICMjD07pN{imHfCY45UmBC4_7wMZq>!PgJo^^NS?K#Lg@F=}Z^a_uhh#D32Z1zY2Q zX*J>b`H56;ypAr3xl=h=)0IjGnJ6U&moLhgpjyleCL_G<FPXSIh+6+yx1_bP?=ib^ z3SIxWPBTMRvXgfckMkTxFuG`6V2CJ_fhHxe5cxA9Tz?a(Al`c6&hAg<B9}}0B=uZO zOdi2qQ5D6maQ04hQ&(fmOpF3j>61m`{Co(mGJHpEdRA~_G*0sbF=vo~`Xc)b>MjwZ znPX}bhh4}wTjOPB($f*CcpW}|8TYZhvk@uM@n?)F3iybG!Pw;SCw@@EvKE4{0(la^ zp|a+S=|AF*z(|A#f9cwydNQ7nnd0#~Cgf%fS0KtpbiD{aUsI;ME?17+xXAEGsk%NJ zDXLLVcdq$8_|i^iX=0LLEvs=kvT)GjU2$b@?&hil5c1<W?tqzPIF68E^G%$p<HMwU zruc0nM+bMWt!!;ALexY#A6ZUPpA7*tjd=XP{&yjGech<}zJKsA{cNv1F91m(&8^Pq z+d?5vK|9(xIX^EtnWNy0h$Tf-pB<zl6%<(D{&vZ|uH*B3c>|C?3L6jfz+NLY*$_#! z_NCmvKjnQn4)A=`+rN~NL*bnq1sywUO50nl+YG_1cVrG5DoqRh<==e2Cz%F-@~?ji zS$!pQOvS6~KxeD5m%(|3Fu9ZPM{itzKu5=j>>#VqoLYV%GYanu;v5Hm*rP!Ej5?{) z5{ls_qI`4_nS6^xSQr7cldrc-xTEZ1N?JKm7yl0K0e?T=N8gWp&Uax=<RI~+N7(^R z|H5fpBeGspdbo(RMvZP-a#$d5146MFFQo692W=6C?5I4DWel!m{4qBKO*!&7c=Nyl zQTG?7FLYtC#t=(c6UgK<OrrhJcJVb`DQmrv7KKO|&oCs)hu8fDPl#=8y^oZr^X307 zzt?nus^|!XxTh;#t5%|IC=t5Tz3amK=gw;BatYyt@R+d4UaB%cBPQ%k3cJH9Ki#vd zjm#>uyM8&chH>@EQOZ7$mj+jxiFhNXHX)DlVYaLcngc`o$dem!ADwoA-iDwf|G)wh zwZP)!F51*vm8}CE8$Airno}w-o8AikhXQ}rPXz0}Ykuo9Ph7XY9-Lg-t%`yE4s_m6 zDPLA72NYh?`WfJgCEeUm;2=eVe*#AT8*$2zBfmTwM5iqOZvvw)pWx!Gh<_0vt!HT| zpvh-@3|W2MvKi{1zgfIrOfZG=HuD{eI*?i(h&6Uj`B_{{?X4oGs857y!3Qmn&l$I2 z%OBltZSLS(dW)A_O4ZuvqGl)}gWQ7~%tZ!8j!1->XaxxR{aoci)7aU5g~PVTh1(T_ z4r%iAgryy{<tI$S3~@i%{7WorT?Owt!e)jEG0saYnWDfi|B1WKXAejDKbLV)>r-6X zeyTS8#%!p<;{*L8*Rn<)6QaDIz&Qrx6<!=09KRWXFius^NXXE?pjza!>5xN{j(<L- z#peky7h+;p<F~cnT1`g4+o8BL=Z}82p*BCw8fn+q-A3oEj}qaTZdX<N@~?DZgJjAb zVP3SVzWM5<`3FBzj@^$aHo|88L5fo;EpcT~7+GoAPkap4D@W|HQkf>N`PD0ye<7i9 zrcl1go}NOYG(n&sR8zwFS867nBiH}<@dZ8!@}2a=z3a`xA)%f5B<Ka5^OGQN(wn?l zu~GQ{Fmm^&Y-wDE%B-LC)WYw#4f@3WKXrZ7K_JnuL$8x%h~g9PEa?YF@J&y3Tm-0x zQtUxIpB}%U`8h^8atjtKgcZg?6i!^8U%r?pMD&ahj^WDDUmUUTQ5hUC`kY|FjQ@xB zR&An=PPL3ePo*h}c5!7uPkn4yg4q|;#Q1<BY}5;Ig|yBdep)&$W0q;dm4W`k#Zamh zx>~Jq>Q7eOY`y;ndfX@IruShB8gE!KO>ngTLxa8+4nj35?BV?e7BEhMFm{pI5F{~L zjiQhTCIfKYmbsUf`DE^G-i<<HPtRnit*wpi;wX6<M6%8>D}U;kIqhoh<bTRd_h6Gy zx~E$+8-+-oo{7*{TmOP#gWx3P#6z<3B5q0#Xr*8hQNq0Z4q*(4yKcF<&4c*B%c_2o zCb4D?NYKae7jdH+e`5??pD|&9hsR_Htm^;>x3N^@3b=DirM;ay=G31M{4%;WjA-xo z=$E1FLx*b#uqEOvOBn;sokI-8nPPT)yU355xMHF(#Z+)}NunBz*iIuQ*aVwxcDbM1 z8&3FOP|Nb83-bi30;AzmOmgP~Fe49zwc%68#qh73G5)Oz-_yjMA@C5eN--guMb7~| zNzD-7J^#uhhnwn8*!|E8o0N#lH`zI$Txd!-oo+_uLF45axaOY)`MP}Jqaf3broWYm zVb9#G-7l>t7COm`%T8iG0$bIOtqm{5y0=;}S$Y<$V_f8(Pcq#rP84+2W31y~sz>bB zQj*}bHfmhTvW8SboNcVLt!}slra1tTuY)vLlhXKI8Tuc_ID<~Ps|WQRgxhQ?46hjQ z=@ow$Lj*#f&X5m`BYKIkDr&z>800%a;knQD;gC>G1!@>u1I3n>ifg;7vmmL)h#YCF z0YA<!nmBSu0#1bGI|9pQxo0Ez_X~ZvxpY4&hZZUR4(Ma>7?n&p>wb|;+MfXkrUo&B z6Nt4kU%b(#1{V8til*2)3xw;ib>os>6*;<jB0mWv9~~*PH9oN|%cG5%MjdjujcK<Y zBLAe&rta=X?*(q>OLlENX)*nl0;Y)nfUR(cZA88_>M2pwO&!O->Q8z!o>Nx;aceyD zExcnZ55;%2@q%&)<BR$2f%+-LQpDXG$a-PzsHz(=ojMM9cv_pxS(IeN+_);WPDgMv zQw%~`0$rNH#nHdZC8K6_D)7;n%lLRLbsRGAp4ORv>?=Xs3o|K+ob{04aqqET1r>R0 zIt>j&aQPQoW2)3@xbJyK1Q1T@YhShK9OxsN94;1?jP!UgY(RRHfC*=56cx<$1YVh^ zSlrTWof|DP@8IG=yY|Nep)Q}(wk=<?<a+Z73Af7km@2Wr(}oA^(n`*cgGCixf_vQi zaXWm!v^Fi{TExMz=i!=kiH?QZDD1As(wsMIoN|-gIAV0@snoJ5cLIR~;<CZTmCp|& z)HN7EqM;k|wlUAvfKlTLNS~=0rKx^8Y_?dA@cX)be>es;b@NmTZnOa17Z{Vz4(K=0 z2JOjkG!q-ss)KYD&Eiv)EqDo!Cr@=P=t6(72nr=z7@x#q%KocJ`_r`SU5e4hP&6WE zyRPEN|34u)XbCG{;=!k7e7B`&DU5$>EyVPc#GS>fw!j%JRn9GC5_NO){y_2$kMg4N zNly9Bah>1GVrlK8uPrit)Jsj@n|u6f+IOKF4vcz&h7}Bg<et`uDkE*@239%MPf4-6 zJ|bG&oYjHmu81nWNH>y5DwsHhs^w3XxIDVxPuQA?>EF&&&m01|800X<cg<O`A5X^S z{BOkp1Tw929iUf5p4AuXq10aQtA=1Jiko!F*6LrbGp55W?}R8z--jEeiw~z~PjCM9 z&3`^5GdAhtZJ_ucJ}($trG`ediJ}r6&}(vbb|Ysz3?&XZH)0?Rj%Z}LljOm;^RygN z`6artam2{uMRy1Rd2+2b#1Z5Lx;P!{C6al73O0C=c+}a(Pg{?38a->YIb<t??D78U zwmbKYbpAmIb4>wGWXIGnsv^u9#HXCZ-_it2Hj-62nIl&HI(guybPyu)IDU-!?L8!7 zOp!xCC$<}dycCZ=i3^-#@U4oscmMhgpXFV6$Fzz+F1L3S6S9WJsjuf{d%J&qxML0| z5Sh8hs0`#Z*}3NsWmA{`3(X`DY)1@13g=ASo|7cSl5PxlzbB6yRFnIP95)TqoudaY zpdZ_VkA+81PfS$Mhl+XM{Fo5$xb}>FBgQ$cA#CNh5vI2;@Ymn}w^x@>>k_H^Jf~1G zvox9w!K;|W2vMnM&f23JI<@n8nV12<n9K@yE8nX%580ye#E6PI0#w>K$(FYH-7m_O zD^GEqsS$(?i%$uUU_>zvY}Jio5%5s#@`l`9QmSpV>s^IKMs}!v&w8=*v*e9%G6pXu z<@Pb7IMkcW-2UcrAK6?~@^6PZNj{t0!o}nFg=uV7>XY%B`QBeOZT`-VE$jtg#H+Nm z6>?!vZ4GJfz7#7w%VpaErcJrB(%aS0m*KOIbU6kR<XTZ#VPlbe%9{L5lcaE6oGl3r zOw84l(-c}USw3S?B_%R&HVH1JlYZ~@(&_;Yr@{1_KQ@AYhsR_!&~d`~T$9_P1&y>7 z^t2hW+o=wmeag~K`w4oIG5`?&q9HGZbp|A-nhDzy7Y~OGnVio{L(kEn4CB-(4wZvn ze4#`{L(09TCw|1E*Cn`c4u%>kNroT`5%L5J>KurOIzMYr9OkRTEnwi3p;>44qEeCx z?`0L@`U|w7g&KYiK_+Bo?GyckhH8q9?{Kp!WOm@>O%V#;zwkhk0R;Hw`9WlC_V-x{ zS23gZ@ks#|Y9=l1NS-wOuGEnoaOFAc8!j-SU^%x6aKTc()EJzSwtGl1nM_ZNX5<$7 z^BrFhBL}E9sGW<wS9X3ZXk-U>xtA6{ES~0?>LTT(7ktO$t-%&_MQI}TJ35{FQM2)J z^)mcYQw=;{J>PY71Nu*ghcjNuh_;01Ik6=qo4<X4$9*3Kw^^GNbUeP{vhyG;i1=3t zO~i{=EOIwApDA@WAC&7-I!+|_g8=X5LDCYbW)v9j@Xo$eP3XQ{rjR~Gv$?pOuVj_# zagO<E_bahn$ykT3+)Y;$sz2s~)JLZX!$+HOL%x-Ptd>@Ufk)SuOG{^x(gJtFp<x-c z@wY$Lged!hs>n{@#<E&0*2uz8qjA|?ud7rqw!8FP9QJ7}q+NCWXt#gJaeq<<HR-44 zvu(X|zO_y>qG7H?a7%y%kv`RZlQIVR#r}}G?k9cREXf?cxVXr<u)poRd&0sG<BgM% z7S*Qc&nLWp0={L8ga=?0;({fS!q<PZm{KcqLNSxVfD(PWKPLuTNkq*zkS4WB@+{SA zm0Re8jwhW13xI-0cMoEuEJR%qgsrC_GY)vY+TNYcJr_JGR~YnXpp*Ja=Tup@oBzDS zjD?HC9TW}OgI*{huib5%ySto#EyDfo{P7jhM9Dd115oMHLhKHcO&SJMq>@rja6qJx z8T~Q57iOn_{W{VRlwA@w9e1B5s>E}B%O7DJLt7BJcXJpiEaMCt>9+Rmg$Sz?bx<|J zJ>IGRD&9%|=UrN5)ZT10!ZfCT?is)Eq$8}g<JyH0AA*~Yr`9_;V^B^C9D>@m=g8yd zi-x_vB;Y1y)~#Qjf3~!DY%-4<aO#%5LrINt5y%7ghazQnmg4DQzOC#{;?!F<E~GC( z?bcQ{`pLjNwq@!#0ed)cx(W>wV~4SI>MArc{Dsme8tJJTvT0HT<-f*ey0&BPg;M+j z4TPv}b5Ki^lw_4ozw<%6%*hwnXh9ROxJ|;qm@|L<SIM)97V5xVD>$;@@gZFmBzPG# z8^W*i{{#UuD0mF@JAwc>Hp>4gBy=~Gp5SgItSRAan<fagqDKEu<@MgXG=ADBDL4cg zlxoWEKK&L_Z)}YeQV??NlA0xdXGuu?&{e2)44<;?rvqV%BsDbVF<^$8$~y%{QiW;B zhEK}ab2g+Qw0Awl28W0ipm22GOaF4?Cem6NDI%<8>>elF@@>y&gMX?~Hmr(@&`;)G z<la!ea)K?7D``##hewRhbRel$$BS33Bas@o;w{f3nR}7OwIOsU)$>Bynunz<HEkt| ztu<}i_H4HF^4|06M|b%hTMrx)52np0&@M_(n1>HQF~u0~;hpgl(?z1&pnzMDabgDk zkJ#}mY1zHx@=tA)Z3U|9#*+oj?!>5YEBpr7#+O>>CO4iVt<<{yx0GtsaX7N>xkL*p z`3@@PZoOT>B@R*`Tp`jU*KOjk@=(5p^jtgE0AWdSnq$uW@pwStoo&yvUFglx1@O3e z?^f(8E$3NAe71|DDIEK^voFgmuX?Q9_TrmHH`&tyRtDqwQVc~36`J+N35D14?GLpO z_EeUP0@fllf71xJ`nn(qar~RK#AK$0Y9Lww4HfTlKtmZ83&rQJCwzZNwiD}8`QC>o zEz=ZbIFgKn6byi|Ct6(9U6Ps(28|TOT=HPeTfdB3kdFdAIpN7I&I%Jl@XvRPy_sR+ zyxY$((S0UpsdRN^96@Fs5hg$t>kVdZ%1*UiHZ_ueqn-!Vc2Xu18`2ZqI9wLs#6rPz zDEtkio%)S!ep1j!@|Wr!VoB*#GUU&Wv!mnEb3fXYu_HiaXwo}2B#U`2r!q?ac6>Ed zm$t%B(0HqzUO8{={FjIN)%|5*M;R{vPJ8(nGw;8^cf7d1c6<S`UeEHslOD4;if``z zx3>d_!Xt{+pAqsnVp;{&wpLt~T+ndgo>Fx3<VFgW2zh6%NnN0WC>N3p3^gi`4i@tE zbT!yJ4*(>EyvwhilvNxw%e@;ce-s(B3weu!5e*4x`BZ)a2wyh~8NJp73zrUpEz2eE zZ7V+;K*G9q&jWeb+YEu)6g~V3)@uYf=V82m+y*G?enk7<vYc3-rT?0^u~6M;I4l_U zX6_I{l|)?~ObyHo`#*)VkRck>y)uguz~BC{3A%yMUl;0^I+X&X;hcuU3C2Drl!&aJ zl};?)!l=u?2vS&uytQy^YH<+~<W}rBy-)o{@+LTO1#S#!UuF|O*V!7=l9Q#d_iM5s z3>(y!0p-tm%TYqUo^?>te82h)7#iyTpA>2|F+*c5*SpgRMpS;}N_Eo20Z<@yhNn+e zci&|y#d;!Er@jU5?S4y2_O^Y%s*Pi#bT|Gt-+BclX<X6nthJ;-t0`>Q-w^Ef^axxO z(WeBtrCKsBGNS}Bnm@87H#dhwu+z`;)pZv&^|9;cmx~SL0U=+U=9@l}cgKp%wN#RK z{;V8(r3Zt@>x!Roj(xu#R)He&l?|5zF?z%|@FWs*R*3uisH85_eSBHF3GG$eC}ei0 z$P(|DN&9CsGEABrl(NSbWfJn!u`sxfSF%1KMXna&a<#7R{pamq9{8Lemc(6DWOWes z*0ZV1?dz}Wuc*8?-&EucWgDM1DVrzXsD-UfQ1;DrPr34?I8iT{b^yAR=&O!QA>#9H zp(9j5Rn{xc-&cVc8#?}s(LoK6?dmDIJ-8GXJh*}LLxnb@f((T?VK!aS%^>r6mM(wq z_LsZ&+wHNxNr@YIF{Y=L;`J#M1oTs9s&4jylyHA0TkK`(t|0vDn%KdQzzw0iYgJyF zHgEzj(3iQQtlx-?QGl{*_g0KVkZ@TQ-Y6KE99sigW|aWnbt$ZO)x)K_$6i>uU5C2; zTA0ejV4B0aF#0L9BP(eVEc4L$h}?}E&bRN&h#W~YI1P==#kQ*#&M}>WICBjyN}ZTS zs_TPDvmo^+K3~(6Q0iqp*kwq%Em;sIL6(38(0A4SwZ>c@9^efq{6cygi!oHrfVV?m z7Sa-cep&;G8;I#h<sO$yEYoW{m~4qlPz{wW%qAVwAar_S)TV**{m#9tl1mD-y4nZ> zx6;Ihn7kbxoymknTWGvu@~;Eea&5JDHottGIr)qk=gF|BnX`&)@IFQM(_7y9aw126 zJx%bo*GEpy2TZ||%W}?xzE+Eqm|tJI)?rba=|?b4WlzkF2A4CeDA`iY3*_jHj<tgP zj2K>SmSuzT@t}%>_WMp2m&ZQMR*IwM?e4+q*7LMO3m#NoJ&<xg6=z$IQ64RLD69Ig z#*))7+gIbq$&kOgj4{Nl0a0%KwUVK3zjL=X%Y+Ia7;0ybahQ?3Ce#S}=QE96@2*a- zOTN-C3o=ejHYID;fY@D3CM8SMR2&=~{G-8lFQ9(7W~hs)G~=QHTFt10f&*6?_k=Wq z3Cvk16BC%RX@6+^uEC~$v1YiASBF+<YICnC;%3_;8~+JtO0x7|J43pb3~d;H8;07$ zTDLs#&Re1Wer`-9u+aTQ!>^JOb4U<CjyCdpEltsar>%>ry)dR~MrchuVAy8=j-KO2 zB~TecygdBhm;|F)^-@{f>oAnL3EMEnWxf9ey^x(O$xEK&R~LpQ2aIh+;WJ#3V502# z7ihFgQ=*K_FPc(Gk5nd0&~{*0>UCf+76OH_GP3ocF|x!#<;jautS-#Sd_!xSdJIaa zE!I)&LJK|k(?Ekc0saYZQz}EDH+NnE68jtngEyi7KdZ?#QQtn81udk;_z4X9V+~zI z)bE_k(uR~ig2Hj(#DfdtY<v&-WiHwRyQAa4ZmPyh-{VT9%i)eOCfN^oXklvR&)&~d z<wkD*UJuSVFqik4Th%;3z9=y{VM3H$g0V=oNL6{0k_PlG&+TRfx`fiaJ^udT=pp!( zdxF0qK&voe8-|cl{HWxKv4HGbXxiWt)1imfq1K^MOT403$ng_fDR!zxY+7ijuL9}J zytWvh0tI_*G8q@*I$@hB&(VPu8u76GlOY|V#kfgWOX%sAFA_Lp1l3o>PD1F*+A@pl z8TOBwri=64tj!_2uD|Ye2N4GLFBpZ(%s0-yOU}S4FWrV*)xLLS3cvqBjHYB{S&}#7 zkr$Vc*~@Bbmdyv^|FW*ZcQZo|5W&DIe}aKw{$JL$rIE9lshX>^mA%FPlCMwobsg4t zG5j~co=}4fn-Q~y-2dF}DEQ4#D&U{WoGLgsFVl#nSY~pAN|TQo1(!*>#blD`3~Wau zvR%i2NfQ{j;-Q3PIdtVK`o9swOhL-3%KwV1Iu|L~&jUs%1Y?Uybh|eVfBSDbtNxSh zB8Z3cvt?3VeQxUR_5c;n2X?7E*KriEpEs?Qaf#(;63638D1Rf9IZ}P(NN=9w7OBpi zLi=SjBY5h%(JY1|#WYND6W-?+htk1hLA?DjF{^Fh`nYXZ0f2Vi;m;_5NJ&@pf<(@< z+(vC91Hj20V8$7Q_v#Up-E+QoTy_aw5>!mcq)+7?hQB_tSme4+TCK1Q!q9L!Ob{<4 zni^gj>E>11&wQrsAuIkj$KML?J`9d8VPC2@MPU?9UanWgWcc1alp1Y8OU@fH2q?GB z`RzzrZK;C(t{d=h4M=g>9znd8$cZrQ9hk^N0EBO|FN7-cIL>xZ>rG;V`(Hg;tpHHe zK^c85cW-_sqvX&@$p}SWQAGySIWF`+v?7Bbll4p#Mc66QUzDV3`vQaQZvrvIqAC}a zGi?kKa&2f~@Yf;?WxpUx+LHE5-u!-AMmOyBA|H$)KYme8OD`|SqlTBc#ALvCIvZlW z28ej&Mtpf)>|t4|jLHWv-`7rDdBYs|9I)!*x@j!QQ;Va*#iNw7=bpB{^#8zZdK-5n zHH?ajNId>3Stdq}lE|6PcC)1?!J?0j+oEaFFkPYvB);SpKneA%DPmyo7Q^o7^g~}Q z&G)jD_#si84KQnAj8)8gOr9-`Q1tRg1Gg|iYxQ)INrB(TF>3QCG#>se-}QtBv%+wA z3s=2&r<rpL25F)=K{;?3l)`NFJRvL&M1(`$|IOs#cBB4L@McJuR;n1X<tD86q}v-6 z+Qx}V@3^I>a9v%u+m`T6(#s+YV>nqnK|gM48B*b|mvFYc6Ka;8_$Bt~1)6^$0MyfK zaq?EVe?UP=V9=z;3N<rtq0;JUWa;%B?VSk*)*)-nMG)-#NWdYk4{~=G2zEW+R+$=< zGODTOM+zP1e}cS|gH*wFKGpBx&%SAb4N+zew)HgpC*3#jjEo)ihm<8oMe4B)X4wm# zVgeb|nlsjKV{sA3<1;<jS)ASQ1Lz{LOEMhD>SSEfcT%>pYGPdzZzWcc+GNQdm3v)% z`;%F~m0<@ialJUM^S&4=_YyVp%SbfEL}z;M1CpsLVZa07E7w|Wo9$*2H}{x^#&UFm z*G@iqEcIT1e@QoYx`}cU40n+i-DXE@VMeY?H#9_(37IF%blW!_Zx(meTOikkl(Ks@ zcBmm%5<0QY<f_s{MdDjUW)Fr{btRqLOnapgxu!@7)cmC+B(g>6ugr~&BVO!{&@w}n zs-T3^U%_Z$2my95v5mB8kVX(RIk4bi!@j8yLy3^rD;m(}joq-l!AL_mVA*xxV&J8u zP(9`2zq_fc#YpV;vKl%d9f90IGFeg|<mkp@qC4W}_YGA%Zt~6XK!HO)x}ON3g6{Za zt{%S=^p5Y>%zEYCp>ty|v)6+4&&aj6i>ESm1Y3&()?A1Xr?RUWEiLqR2Vl;Fi{Tmd z`<=rqtsN5F)Uwi=E1KF_r(r{*KMLU;xiVm-8Fk$2vyV)9`pa{a4ghINHLgB7?l1T_ zX6!8U83Hl+cQh$i-bMh!>WIfb4I4ak>?4E;2Jhq!5`n`iQM)Z$Fts?;7@yxZe|NQx zRa9#xV1UWsZp~^d)&3?`8^$2}k}^2;bh9MPaOyzR7rK`+mi`cI*{zYH28ZfCL;Qn5 zs_s!Qj@NR2EA%`x0$`m!<{T~lKH!|9v7Lans%t{*2Y}S}g^~T8O&#A5GBJu9+JKC` zllX5|MXI^RA$Fjzbhv4SNi&1H-SVGRv?v4%(V<(}HfEO9V?qXTHpL|Hcl1Hx(2fF3 zW5a9qtpP<hZ5<PS_29<hegc{7vxA#qls_2@Pt20UXmDoV833|wI=o?mfp;pb7SdA+ zE8o6i83+Xw&F!DYMXf9U5VC)eOuVVrwSv97eV3>4^x8)mxVhcmE@W&8UqxZf8T*Xp zomzDCQc^X9#&v~+#>u6G$$W5QDolp-QcLv1D9HtEdG*d|p^Z9SSr-e}`|<9s_Sx;F z%L4X_n44sLFMxCH1TGlOsF+n9Vtj#IN@^$k)w@LPjdipmxH#%LnO`5K8y6K?;1Vy} z{IOUMu$`kQ4Y4-~$pw$SbyP{7;om!lw`Z=0;xC43eK4n-iQ_7XkpdU?YIJZo^0MT^ zl}_$Gi6Qb{W)AE(J%qFe7T?d`y{C!BD(_`Jf7MN^26}}_!O>gQw*xrNwM~{Q*6bp; zTv3yW)wFueR1{@ssf4xV-jPyXk)Xjd6)Y&D$p$&-uPLgiKDv!g*3dVML4TvPDr%lU zo~&_fA-^NOnI>#8k@cnq<L`>TK-1o=CXsb<>K~N4(AHEXpPKK*4atHP?r{=ajK6xD zP>~7^0quSLT#GkhyB|}3UDf-ReBZRMC2aC`B)}Y3O2D=!FJM@b>Qup4+&ZwQE9TTT z)@B6PE^vLEsaT4gYZf55zl;6};&?fdKP`5_uvc-~3Z{!EO*N$SB9vEY<h#`e58U>R zf!9Fgc}#-%Rhcyn78}l`)M&vf?C%pdt2gYJ2S~ilq#~CgMlhSZYKhGhy~*!*X_QRu zRuCiR6AIOmO@dc95UjHQJ^DFYOf_JD7_q>D49%;9Kf+O*-zZA)D@!GT32{iw^pBkD z&67dS<@ct5p5|t(uNQT?M`d<qi96t~ytFNC*Zac>x_^7y1km_9m$GX6Gh-tO7A;p9 zGJqUt=CHvO#6({Kzk&{i{e&=Cm2$qw!-hYnn~uVR-$UpK!O0AhzUyyESGAHQ8io5& zHNx(Ds8nJe;w34F`AKE?jXP`pTB_ynK52_b$cUAOC<zm1@7?8z3RUa#zY~G_XC(+| zD#JIQuvYisJN{fjQltl&Z0ZQcEaz*u>I0|BQ7V+~%A-Oy8~NCSQoqFnB9)4yn4}|= zl2zO3D+|4_ik4J}9%YZe%OS|tc>hxFu%kvrp3;yaU7w`<Q&sf$Uq=~zP8Df;fe*XV z^yQQdsd_s@W1cO=^nMK-*bg%y?cK+~U2{IGd*)jI)?OWPUY|PzvC@0c?~KiUB4Qw0 z%jQ`-T1sPPNB)Puiat<bcN1=XfF7~i67=ja$`VK2e?+OjMZ?KuUtl}e)Rv3%R4dDr zv)J4u&ZIKWNn_uktpmX@8tHBEtGO*q7yAyBXVvuIiB9Kt6`ag2P{LnILF<0#XU9PC zy#>l`ZHN8i(ic!8!3ttUuQxI_@CT>z-hSa70kknFJFGIYE{-fWaU+q{N;!0x<KOvm zOr;wd+G<On9h;o8Ja(^Q5u&|f-R)G{oeE22y29yvUTI4(+8j=@(4Sakt2x@d1+Hb& zG0GE9ZEDaWW$8*>u&@HdYr}N5E99eGX$<1<<mG$0Q9(jfVQy1o$bu#XzFIWWUd4L` z+GgW)FFK)GJAN^%m*|!x*?&PJ9}W~4hP%W7szDK6e04Dqd!;<NX|o$#-jlo4^1XT= z8N=(L(e`6qrm%)C1dzMAAag?Oy00(IsS`!pG)U=VKB05Mp4|z}{$ipm-ADGXKK?DK z?!TP<*${J^@pVU>QAno&k!dcS3V<Y*580|8?@cw;woi$KH@Jpk48irnpC-YZakhkg zQ0<X&tqi1`XB3f4F%Xf~RIit8a0lmR&6SL&36f{`su|}MiyaBwbzAo9mVtr09Lr$y z;LCo9iE%HvIT!mDHE^v)o$TA(uE(<>_vvKhZ<{2~oNL{x*QNp6aq*kR@x&~^JU!}Q zcueo(Y^<c%B4WH3A$?9zb0Wv+JEMo%>N8DTi_V(*x`~^I2xile)m26|j_PxP#?gY( zoBcE|{BW672y+^ZU$MPaw(G(ox7{Tf$(Mi%-2sm)5h9z+@@AP#ipig*7J;`h*Cm{K zybnhE;kQ#t$gqS2S|_(5i!xly!!JJV`i@1OZLiWlH24&(K7jv~DIsKt(zvCNf`Q#H zCjO8n1#BEvSujJpl~#f9*KA6~KS|;<kxo_&3TjQ~*DKs8S~0s5oY1^y-51L;w7Mnm z^x!l&FFY4MQ>+Ci*`>c}irG`|aE*k-i7Uz&Jv;FWY7DW1$*vp8+(hWL8(3Gr#Z=d9 zbQ9PWdC%JnMZ<>Z#LcdzQKLdAEXkvtjfXB=0>w$PXpk+|SVxkqs;vsY5Pq@9LyCCD z=!jM;8jaNNB~4PU!)at<|I(5`I$E;XILe_uqT?2s-z~;dro$Iz^ctMp5J{l1^lvFI zA|FTay>JvQZBe_7*dlna`g^094pky+t&LAIB2w|x^u{;>A&OeK^sdymMX7umu?EwX z2mET0>BE85xP^$?gp)&M(Pd6{@MM(~`Wm+FioG*PO1NXol1(|fCB`iW3!fw9$vS<I zxblkAj*msiYpd@SnQX$|`(zjLRoRil`O9rgLzQ6reNGW3YSOZ~5t4~->xZdS?Z2GW zI(^wE%a+Z+EHpNbLi3IH{J?{L`aQfAU%<2|y&l0`L6sXk1pj^D+-Mz_sfbl|8T#B5 z*HfMTX~LQ&<~!12kR{@qixZ+%8P_q@>HBt8ubY3Oxx0^Jt-J5~nqF&zYt3(}SzG#w zi_Z#3Wh8S%4UmGF;P@HSZbj3bk&S&A=hyph#dKNNjwe!_$jP7eNlRb;=6K_}*8?t2 zYvf<wj0k_S#xhq9`{}Cf&Bnl-_^rmyv;EK`{47iAsf(MVl>aOwG9C_CW;K4eaFKm4 zTtlpz2D;!KPoRWPp(*`+&8hyNK-6glO*7NThp9o-1&gn&$mV%|UJEv@%Y9$}a<mLQ zXWTU;4D}bRhJH9=w!^zO+!26*0smLBl$CuJ`&Zgo!q6-a_Oi6R8>axA(Oh%M2F|2+ zmt=!OV^#m<j7Y+O?c0txi1GakGr9C&jQllARQBuHaPDUJ2jix{|I1rT_s5xG_v_nu z$y|w{|Lb&1_j@hywtqMG6?lKWK7M^XP8E8;+CAKSe|diEe!nT!0%ksPh`ZmeQa>KM zuiu7AHb1Oa4*@>l`K?9Be)#cWO{B}W-gOR0d!F7TAqHOVa=Sn7Pe>4+#^UE1Q1L(o zzn|m#;v3>TEe1>;YxHohcTlf;!!JqN*mmrAhHA{ub}rK21v8qh51EEnFJ~UTyy5&6 zS!rw*cAn^xSSeY7xRR|><JEqs?v2FwAH!xSPv6hprJZwL(`(A>d2UPuDsN_@dvKe2 zoBwSyS=>6lfA!18bg|Wdp_Pz5kvu!$Ui>U1IT|9YZ|%*xIot|;@19K0<)s+YAzp7q z4*t$%&$AIDW3~SO)pZ@<RQB)Rh)4=WLgul`NGcs<@0CP4M#J8Fug8iAah#0ootb$k zGcz1|?d*M!WY3iU^BleP_Ws^~=el^VbH3kueD2TvxxeS?>B(0kv*2x1po|bUvV|O* z*z~&Q^U-KTxG+92_Q_3ns+G5`T7bKdUOg0rJ#vZ=e9Dr8vl+#3Q?IY+-UsDnIdXd& zQ6{_DnA!0-)F3&RY{x75(|+`hA7<nnKH1GztFd^F71Lr+eS<B#F>@>6nloU8mhx2- zwSsf->j&3@d0eh8u;|Ubxu;;+w$y*|l1o=0(;ZzPHbNJ<Yp9_ko_e0!(g()kN5Tgh zZF5Kux-Ql09J(IVzx+Ib&sP5QTXuJ8j;tFel%h&aNTidSKGkDbp0nKQan|tK-!_@; z5cd|dGrSaC;Nu#kz<Ej-4m&L=O|wN4<a#&KFwJea%V5UYH3U)6S<0Hhm7P`4)@{9y zirNBbvceQI;>ofVt}WB3rD?+8x~#cI0>#Dz!@=`LOo}v*Wd#a(?_6@J*0)`4eJQ~G zi8bBI+D{WzWz&hm8a(b~xF=6mCc!IQP0xGD3eK)lt!QtkQ7mgVJVKDmRl<Am3$l62 zrM6j9H#_<*o4nWEn6fO#;CBeSBQr|gI-USvZ`qB()5hAG(&c_Gm&JGtx3zaUMqq%( zB#EtU@k_OpBG+#FEwk@wdU2y8te+s)YSD96Ay=cDMQ7ojVrmt}aD?MgghYmPWnGQa z*U6kf*Gg~7HFO<`i0&iim+MxK#NMW@oi@xCWjbMsP5pdY&2?Z&hpGETZKtCDyHP-n z#95c^L}DRjWQRFYFI@F`_QKqCt_=*8W_ZI=(Wwo&Ek0(_SsQwmR_kyIbF`eqWuZC& z8od<mu#f<3DMQXkaiY~zr<Yh0rL)^q{o^~buSXA9r>wl8ILp71878wG@wesHO&Mtu zRq!R!+3n=pldjv0*wwM`j}~R_06(O16?Ez!XG*TWw<FaFn_4PD)=&;;*vd%dkIJbO zn=RJ=_1PVnNEsQLKFCjNu3qE7Cpkcp_%)9ic)vI%)lHgWL!VaBIY_dx<Ysn~MHVT< z%0pC_BRMVRi15#&Bps@24mQkkw-g=<8zpcGEU~uaV0JPzLXOe;X||R<1*(_H3gS0v z0DNY?;i!qmQ?v3O@2Wc9FfR;B+op+24s8x@POjv{jFLCE+Ud8pX&jw*vef_X%`W<d zTo_5_K{n2R>e}FI(<X}}r<)f7=6}Gm&(y)l$bQ^YskU2t;JLtmhjh&*fqO#qMl7em z+FLP|6c%d5qPe}RNi7XAOMd|$x1R{NH#U_U=;$VUpkX!D`~7Y-R%kSdpRO)5HG9$M zwXDJyG4SjN8k6ZKI;O4}S<>~D6|bvP=-M*vKMy2;cjHwMY1}o?y~dawO)ILxFE9|Z zWc2mJ`V$>Qcgz{GNe;|+G>P_T538sRNS@X*@BMO+x@hp8hByndGGOg$VcBZwJjtmP z+5AG@^w~ig1=*19m_1Bf^VAcMkX$qmr>=Y4eVye_#|7dYyTI@^nD}$paFW@NH)l{C ziJvC=)!d$8GG^`>h3jCZFTQa^d0uv3{66Ou_`<eweaWUyPXh)Ql<65rxyFe$Nu1?s z_g{k{0`jO29#t4lu>cO!i9@e8$2PGf*TkQxC6L0}-Pww5YVXdx6H=r|=2$qAy1m!N zqZL`-jdV<R6!*C$^}$dh?wUW=r9<Mf<4h}xlJ(o`JqE28T5Tazy8N(x?mj^_{Hd<C zr@=(N%P53h<wJ4im++q0izyd2UhnZ}W#nBv0QQ><2e~@F^|=FcKDSDriEuo9FO{vO zDHz?6ST+43a#+6QWZu>TxAcyWv`o2JE?dOPGhm%_dc3R6_hginXKmB+J;YyzUM##I z0|IimKHNsOn&$Bk^OKYIEjLNWP^aXc?-NTCb7ov%qEec;dGBzvz$vWFzViuT7Hj*k zzjiLt+33@?w*Fk8ou^LqM$0W-kG>5Oc5%CwxLygcAu69<X?1gZ5OL31aWH+>Qmezl zdg*)mTBQ00HUk?sr>(?IjtYgj-fe)*_zPIa8|Lx0YdCpj_YNoVCYk27oj_Jj*3A-Z zxG{)_a8WDfqhX+;g&?fjBN%{GO$?uI#MA~`Y&2*D2!)v{XU@a0>|#PZAa(YgpS_g7 zY+Qk^*^=v|)TpavWbm;`y{>*4IcQDEC3MBN+&Cf^tr7|(aW+P()py3SjU95a+StgV zh?QiK)z}$DLGn=XCLYOw_$Bdy0_#WA{Reh74})EOH{Al`+hD-UEXY_Q<@EMGY>s^f zn4-F;PjRv`<FbEB>Dbz0?Q~+~23C3Vg%qNid?&WIEH&0H<6<fE<4K0O5Acc7UgjF6 z+N*Bcf=wIGd^X>Yy3{#I9ITTlyinZvau-<su4JC7Y0~a~ZknImU&wz38+E&r@Tg0& zJK+qT7$f2~W3<1}OfNRaUhXAAXIC!R0gzlXA<diRXrHa#K4nq6ba!f&@)oqpzi*IU z!C1|zv$fZQMrNw#{iuDJ!uEk(1nd^mn0sIKTPBt{`Drte9A@&c%Vp(X!_QQR*~Fv@ z+aN`GN<JP1+@27<givOTKCDUu6E2&p36Y2+!uPiLYeqFDICgD;x$WWET5$vbC+NJx z+GjGQPR9j?BqT3y8(`K=rP-ypQH&NnI=d6^cp^SUrl1fWNi0XL`1NG2gp4A|GqaG~ zR+Svvvxih63Wycqf&Dbc5_hF%?vCpwDkC0R;$$A7Z$6t!+WNVK`ZGM7;m_oF=pwM* zie|`p7jGoXpb?|Hk!!jO2*v>FyXbhG02@Ye8$e_>r5~!|6?mgM4WX{dhaxmZwWO4A zX;~Iw$Q@D_+al{QMeyd~pA5BS-8}nwEymu#NFvf;ryy|Lr>W|j=@vL5OAC}gk~7e1 z!QNW3bM(|`j)^WkE`JP%i#W_NwR}NrxC{GtfkTq$3WgBK2+efXb>ZOzheR|NLM`IT zla3K%WhqQTz5i`{cs%6G0gdNXbrqxgjp3g46PjGp(Df^ql6HAgB5IrkIG#?YS2BJ! zc6+QD1&$l-pe3@w=xDZcZrKI;b)rlx^8)SPq(Dx<-afaPN`jrJDQo6wV($#C$)L4W zUTDh$76PM$Vtrd-Wm&fgefB&3CmNmY6apHx(zq6P8YPaLJFas*EU;9+ah+xPo>4OV z<MyVbY0@m5Wm)y}#b``#&Vd?xmY+t{Tvy1aXyW$_QC^!oDf50_*wn$q%I?rvNB^OO zML`ZCTMl*WFLWRz)^2QAeeakz@_B@u?Swx7jC-L_?Tyrhz?jNfO1jEf;@+!_Bk%_> zxNR{pipbs;zsFw0hQ{2O+)S)$Gkf#Fj@OC!w#=(GTWazZmQd2us%V!mc*~82ttrYt zY^weQY^+96>ZAXh%a{b@(A}2xRF-6poz}bl*TT6m&WkUZh!;fVFLhC6Oy;ry?-#6H zn>z|cB7;m7WP5`|+CQ(;%bn3qS_l}8%L7s<fJ=!1HSpTbb;@TxG%}S~oK5SvXg;Rk z-7daub>~H{k{?<P&Ji0Aof`Jx_zT+A5rsciR^!g|2@ovoP!Z;G3Aj<d+E12i*-ljs z&og<JGf9(Jb-AO^|3*FfEugsRa3ZD+wn~IU@zHq1eefu7p^x26fK~N*Ljkj?s;&0x zShnXKhDgM{HmtT1sG#OeuSlQh|7GcQ(h#mu!J$m1%&eUSw|pqVpIrx=s@~@j;E2C* z)AO4+XO850h@nD8w|&MrSvcwinK(kFMgkm>>iAB&ie5<d7{JrPf>G#@N9D2mrsRyQ zk)G)foQw8-suZ?T+C5gjqE&4UgxfRqu-&Y(f<uQBs5ThAX(2zs{AIB5k%QiptLUU) zen*u{{Fp|<!0w5IUQZu4cq2Haie<ybpi^MaxQ;-$3(pmh;^?bn^-NbfhkM<f+~B1J zq8tE`ZeUv>Z)7Odko9m$K@E7IYUDX|Dk#uLGfdB1ptKbUfdmGb$h)4#5(00jRpwV# z-+sE>F;;CIYVHkBSw69?J<g_b$ckqZ?sxrao`OSbGt<?{zSO<z6(_Vzky-P5RjE7+ z7)#T_wfPUd8*%4Nkakr`ilE1FXCWRS&4k6?aj=t0(|g)~i@V4|?<F=^<)qic7gHN? zbK#J7ArJLJs}hM?28KGR${$2$)doemXhoJ`3pdZSE>r3;Mt>oC`O#Sb1-2>%Ir{zV z;M(}Cpq)FeGS90Du8SuLixbu}zMmH`w`2<t<vNUNjFiKVr5+H|OPm3`#h!=kl{ITh z?|&wynRq!M=9B2Lx>pA+Z$BEpfMbPMNL6A>Lo8`*LhWfJ&s3>F4Sy}S+lg4)D@@aO zte{bt>HH`=pqh3M9I5M#;0mY=P4h!+RidCku<8{8b|Mfo9~a}~`l1GL0~15QxBR6W zh&jWDiw!*3>skMOut(c~=Xy|U>=34t#+{UUKGd&n0?$<;9Ra@|@No(>&$GWixb{6| zM4B-6<vfjdchVz~M-XJ^Tc1C!xWpgXk+}G<^aTIHUCKC1%^REuuU6fYj>&|@LlaKI zjhHtC{Xe@Cc$-TV2PeZ9<cB8a;nxq3O|tgw`_fV>*)XCU`}We1&wWzr_gBe0!gsUS z%}O{_pJSl5*o+<bsr<r*Iph$vZaod*RQ{wZnH@W?F!1WOGDgX9u*Rk6rmcLOyUK`; zL3hB+x69_|;}CcAl`)9Zts^z5dgS9J%|HQL<wxe6;0YelMvSeVK{izI`>`5jw0r*S zSQr_mfOb{cHu2IEY$d7sD7y)+{l+ig?~hPzIwT4|j3Y9+e}7r|Mf_yM)y=rM3&LDb zH3lmBVZ{8ep##MwWlV<y+R0`E7s)BK3TEVVs^4AN-gmrGD+4{iwKh#%(8%Dh>###$ z{A6+R;Y-|n(Kk>t_<#YqZrnyyRyx*k$1u76J<}7c5TC$3I`B-htc)>N2aheUh06>> z5C|XDg59)8wyMLRmf1JZS)_M_%8`aZ@1cJlzh_JwMCt{hk9`YlHKNA*&sEZgFG!U; z%IPD4%Cr6|BL~8cbVO!Fcb`kU8#H6#9qwC$-!UDOc4FQ%aH0IJ5EDk0Zc!zgrztaz zvUE?W7w87u%1yi6)wa$RY~E{Ga%5iIzC$0@^GNK*8xPf!X6%k8>AiHutujkVy-#~j zW-ap!FYsdG*PbLbyrCaZ8C%Q?L28|2&*xCO52&IFUU4CA+%2`>;4b7)Uy!?TZuG51 zy3v44`|nlIB1NTq(&DPSU4ceLKR5hykqNTbf?(EKQ!okznf-0`cu{<2`72)&(D&i& zpH_k2zSsIe-(^+*pYPM<ce`Zh{@Zt{#Z2YL!TcWAh7k*9a!!Sv_#)E#7BeDQWbEHg zX{*$9Yv`ylhlN{Yk5>LGIslg_6khcMiAUoO1I^VnTGBMWMgz@Ue=h|yfBT$htAt0J z9D#RtkiHZTmLJ4}*PgdjCUGb=)T+Qnwpga!Wx2?ya35TOJl?;Bczighxw*d_Zt;QO z;K569i!T?wxe>m?&E5|L$lfTI3uKGm01cbWC?X2uS{5>i(uqhSW`ykl&sCotbHEnd zWrJG&<r2J-&=^U8!gO%}mos;GKye;#gq&e}e}D$IdN_u_2-6FMKu=(-zL^C=v%%3W z+8G~tDr)_%Co&ILa<>yi@`W9wIwfTRxWp%w90nr6D8L`gh#TO>`>H8KGrRGFZ~i){ ziNZymAGAymSwIKnp8b(vJz9x=z)oM{5zJzN{3jYv1{P58YGy|@EM$wGGDUPnWXzwx zpJ>nf^;xhf0NVHd&<27efzpx&z!bFc!6>&b+kto(ir9cTP#Cj?gZLij2Z7x9u@$md z1}_4$l0ksbL_+x~n%jOg!1o?$-G;D*phALI;Kn{vzx8v1-xff~CSTkC0hU6C$GLj{ zGO^w*)p)c}BoK5C_KjH(f!F)=E)%M#40YcT41?`z5twQEak@WC-LPjKGtQ>U<Y50F zP_-G}l%oTk*_r<@9MZ<PT9Q16aD46Rx$5J^Uc;et3^M@YT&Z&tO!Y5QH9hb>865sD z)XZcGN^b~UOMJz+XRr_nAlBfrRv1hWBWqrbv)T&cNxc(Yg#+ww{=l+oA+F+|{euK} zuw|&A+R`{jIk)sc1fSvP20&mUODCFNco%>aF)qhZgj#78#fK8((l_NU#PZSnrOo#{ zv}1}x2wR@3UKKjH`Sd40ppN)eXF&s8ot@H?gnqK-SI6YF;EJhB31JMRTS2TJt_isd zajtch=gTTsH6{zYKTuX&(wYL}a6LZ;@_@Mm#2OD7ejOwuF0V>@9QrASc7K*y$rCE3 zF43Q?!QmG+N%lz`OdU9w^8Htj4JPc#25d)Vuj2nV7{ovRgB(>+KfXp~Tkq=sD&a4j zss@fAi7?|lbeRA<1ghfSZ8ZA>*!<=R)}D9=XY(f(EMV;yx>g=m*VC2y?j*VX35EF$ zkQF;YuA6@dN~BfhHiFqkp<V#YYGi)=kCtHOUg^^3N%fx6|5f;(O$2z;Jgcs^g{Wjb z^yjA#D{hYt<@cZ$tZgAxecB5OYSvk$Ap}kk%jc{9xAGLO@X1Bh8U%3Le00qvQ}WMz z?H_GGYB;hus&y^{hXMeaI9AdzMXwAnT{PAOmoAtcUOPy92@PSW$yX9ru%OvdE#F@> zgGAH<{K(pQ1?1g7NkMP!O+)LNFwtc`k}#rm&UO%|*?;T9PcX)_pjrXH)#SLZ$P0yv z2#oMoXjh<Lzduz(*v`cCFAtc>FMzTUbS_is*Ko}m7;7OI2^{_@Y0&V%=_m*-y*Lh9 z{ix9>;-B3C)&G?kS(q;6YF)NDFl#WB;C6(Lk^{G~@yh*HUz2uvkj-W%4URh*qD%s$ zc}q25gO@88<5+vM*8mjJV~?=oIL3y4$AObkIUTUC3f*@<(if4L**+2ib>z7BAqoJ< z-U%prxuHJ@bF86N&a15;;HfKi|I3W;E+8{pD*l@pO$GnOjEeC^u%5bgXg2bnGcuA0 z0Ot*elV^vGW|K3%b^qsol(L-GcmN!1W0#zFsdYSa-$l{b4&DS~{v{~P+(|-UFQ6ry zag_O)Z5YJ4V`{8`k4uE8ajZ+=PN+#9k4ZWmK@CZ!A!G|cVBo+9!B)Tth8dv#AL7}n zFTA@8b&1&jV&N?_^yaUTpt2S6%;^)k>K-{xX<L#@!pgW#b?J8cqkoTQt4$KFx>tbC z|7nuhA5ql{;VubIwbSpQ8T3OAY!L7gH+W}@UVO`+e&>r0cfx5MG#Zdy(3p3^)p4Zf z-*ps}J?2GJ&u?D*%}-iaKyFMNB*chL4<x7QR^VZTc^CxeClssF{{;mv2@Z<er~d^7 zaw2h-F(iLCfqP`dS|0r7z*T$iojUHEQU1#F!BjyKNkWtyc&@B;$|vTLXwSnZ1Jp|` z-tpMgSx&y^7T)5Rs4s*Lmyhw4?vp?8MQZgK`)yW*Ois89$_86B5P+p^AV3w+72~nz zXLk}T*q^KaKHy7~O;~lCg&T20pa%=>>Y^`h;K|y=GSb{KywLClWtG{Y`%a6HCe^4G zpG>`U!U-`YYBN(=FSKrA4S?JK!{HCmPal4x0wLK|aM*x0!>>a&*;a5iC6*yK+fWJ~ zII~No4en#jwty|POA7oMRyqOM1+U6b+t?!SlOM-iBf;+#e{uuQ1s^!{2!iCIYLyu$ zh`W~(GkPs-ga*gi!oQtKW`XqJtH=i~u%8;dPcFPJe>EEJ4BXfYpRzhx>up*1#7yb< zU;ya@q<d8$l7cSy<xxSCBw%94yFuab`%xFLP`z_0G5vOq{iGyLXh6dNS3Yq)B}PM} z*XR5C!tSbgXJyKy5BPWWzaHjj)eqhFeEQ^Cek#Lbb|#6E5v2&GjdJN?-mHO<Y=Ufk zz~o+K@b^hOpQ&d1#;>n(1eELau%9cu#$syMli*)`g7dkVb~!38Wub)<0M=Tzk_w{p zFIhS68>sY}Hv+tv9ICY9Q_@j`NT;g_N`Xi#>2{ab4|Jn%y|B;<Q7>09;A+=ZVl-AR z($0M^S~H;J`QU|8AnRp<r_N_-zv2reSIJ&^c9}BtH3`imGSGKNm0juybt@I!Olz7I z?rl_gR^S<zQxO`z<0S_n(zmB7;IA$DD83OFx>?f+-!ZrBociX+ih&6SAAZ2DYB0I% zKEZr!x}I$7=-e(}JGFh!G;c{Xnen6YTj0gbY!B{EjiMKesNhP`!YHAE64X59BJg+< zk-fW*+H}|>D5EeNB>*%7U*z}V<KfYr!o#CDz6sj!iIt(W*%Nac=;_l|F{5e$5)?tJ zs$M5ccVljse0kOTkk*^P8&7ITsxb}UU~5J8oOM&1YV?^e;**IkpP0m}Z|*;v?LY0! z(ytZ@+ZnL4IB;Z{c=(91Mj`v{mpjHHTO|~a?5WzCzK_>k21Y;U@+F8N&L8AbY>Pf( z>~qTd{OM(%O;xjx`-WJUKAazDa}?HZeUO|j-1^q?9&eU%wX6igj@C7r8N^d&q)8fu z;<wF&hfF-@>(AP#z*S>7q@#n{kM?UjJUzCy7P(hRr>A??6EogrpmZI|lmc~?(ASK^ zI1NQF-ZqKQxd^Dvb<nEI@hSiD>O7!#xcJW(Ryq3DE8t4oTZT4=WT~rghr|qf$7cFe zhMzAC{$!kOORGAAP|G#N7{yW*q%l&Vl8}(|%B)Dk+!~_Bkdk!4*RUbkNV$}4IFc@F z0C=VxfDrt~LrHJx{nYw2r7_y8q&HM2^yPg9g%&?l4K*x5!$t-ZDM^P%*APF8THtwq zdzXHfbtql_P|xp-`E2iu$?U=G!`XwWh-ucDplR9e(c~-di}St^qcN=pdagHxNn9^8 zj2CAfJ#r4V#q|myp1LyOiSu~GcqDky%w{KusQP7MRm;Y$uHyp%aH&zh=-bZ---piV zD%ey$j*fwiY)>YjbqlS|ig7HA@h|8hi(cfNs(b$cv3xp;OAg=cAzAM3MGs!|^eLi2 z0p15%mLJql(iCovCZL2+L&V9d<G%J(R#S>28WTo~Q+m<@=tI2F;SVC^7dl6{u!lWg z?3?yDM+wzt+(M{<TO4tQRVRbGWMnuqr_Pn`UmL)qV2~y{RbCb+bK=P360Px@Gp9Q+ zxtYs!lS}7ayJ@`6bME$Zw0}Zd_wxnOiiy8pt<H%}v*2>BzjB7_4pGh+o?}Hj86rl- z^}S*76RnmrDmiW(yZHri&LZ{O8va$QMQVEQ4(X+{)>$b0&cYSjluYNXOWe88Tz6&y z17W1I#!(dS9%wl1_5|+<P6jjb_j7ju&+=J0P%u=1NwXmHo%I&^cJPKFdD~Av|Mvu# z-7WfmKhYfo{^y^mXjeO7f<emYG)n5|4|Ws`|9A#69_`<&aKE)KMl)Zei#}sd!SL^Y ztMGeWiDa~}JqN?@dzJA1Wu;vev_c&nWzSFW(k8mmo|WO>J^$O7o;}iml>x^GP!pU9 X@bD;qe+XXbIYx^)un|u=L+}0{qfPcs delta 23382 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 Md=NjifhT<Rf7MN6(*OVf From 928e8736fd4c164fc4dfb8ad7c7de80a81dad1ec Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 10 Dec 2021 14:52:27 -0700 Subject: [PATCH 026/112] updated processing code --- builds/data_preprocessing.py | 79 +++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 23 deletions(-) diff --git a/builds/data_preprocessing.py b/builds/data_preprocessing.py index 0d19e221..10f29af2 100755 --- a/builds/data_preprocessing.py +++ b/builds/data_preprocessing.py @@ -759,7 +759,7 @@ def _extracts_hpa_tissue_information(self) -> pandas.DataFrame: filename = 'HPA_tissues.txt' with open(self.temp_dir + '/' + filename, 'w') as outfile: for x in tqdm(list(hpa.columns)): - if x.endswith('[NX]'): outfile.write(x.split('RNA - ')[-1].split(' [NX]')[:-1][0] + '\n') + if x.endswith('[nTPM]'): outfile.write(x.split('RNA - ')[-1].split(' [nTPM]')[:-1][0] + '\n') uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, filename) return hpa @@ -786,36 +786,69 @@ def processes_hpa_gtex_data(self) -> None: # process human protein atlas data hpa_results = [] for idx, row in tqdm(hpa.iterrows(), total=hpa.shape[0]): - ens, gene, uniprot, evid = str(row['Ensembl']), str(row['Gene']), str(row['Uniprot']), str(row['Evidence']) - if row['RNA tissue specific NX'] != 'None': - for x in row['RNA tissue specific NX'].split(';'): - hpa_results += [[ens, gene, uniprot, evid, 'anatomy', str(x.split(':')[0])]] - if row['RNA cell line specific NX'] != 'None': - for x in row['RNA cell line specific NX'].split(';'): - hpa_results += [[ens, gene, uniprot, evid, 'cell line', str(x.split(':')[0])]] - if row['RNA brain regional specific NX'] != 'None': - for x in row['RNA brain regional specific NX'].split(';'): - hpa_results += [[ens, gene, uniprot, evid, 'anatomy', str(x.split(':')[0])]] - if row['RNA blood cell specific NX'] != 'None': - for x in row['RNA blood cell specific NX'].split(';'): - hpa_results += [[ens, gene, uniprot, evid, 'anatomy', str(x.split(':')[0])]] - if row['RNA blood lineage specific NX'] != 'None': - for x in row['RNA blood lineage specific NX'].split(';'): - hpa_results += [[ens, gene, uniprot, evid, 'anatomy', str(x.split(':')[0])]] + ens = str(row['Ensembl']); gene = str(row['Gene']); uni = str(row['Uniprot']) + evid = str(row['Evidence']); sub = str(row['Subcellular location']); source = 'The Human Protein Atlas' + if row['RNA tissue specific nTPM'] != 'None': + row_val = row['RNA tissue specific nTPM'] + if ';' in row_val: + for x in row_val.split(';'): + x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1]) + hpa_results += [ [ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] + else: + x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1]) + hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] + if row['RNA cell line specific nTPM'] != 'None': + row_val = row['RNA cell line specific nTPM'] + if ';' in row_val: + for x in row_val.split(';'): + x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1]) + hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]] + else: + x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1]) + hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]] + if row['RNA brain regional specific nTPM'] != 'None': + row_val = row['RNA brain regional specific nTPM'] + if ';' in row_val: + for x in row_val.split(';'): + x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1]) + hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] + else: + x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1]) + hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] + if row['RNA blood cell specific nTPM'] != 'None': + row_val = row['RNA blood cell specific nTPM'] + if ';' in row_val: + for x in row_val.split(';'): + x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1]) + hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]] + else: + x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1]) + hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]] + if row['RNA blood lineage specific nTPM'] != 'None': + row_val = row['RNA blood lineage specific nTPM'] + if ';' in row_val: + for x in row_val.split(';'): + x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1]) + hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]] + else: + x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1]) + hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]] # process gtex data -- using only those protein-coding genes not already in hpa gtex_results, hpa_genes = [], list(hpa['Ensembl'].drop_duplicates(keep='first', inplace=False)) gtex = gtex.loc[gtex['Name'].apply(lambda i: i not in hpa_genes)] + # loop over data and re-organize + source = 'Genotype-Tissue Expression (GTEx) Project' for idx, row in tqdm(gtex.iterrows(), total=gtex.shape[0]): for col in list(gtex.columns)[2:]: - typ = 'cell line' if 'Cells' in col else 'anatomy' - if row[col] >= 1.0: - evidence = 'Evidence at transcript level' - gtex_results += [[str(row['Name']), str(row['Description']), 'None', evidence, typ, str(col)]] + typ = 'cell line' if 'Cells' in col else 'anatomy'; evid = 'Evidence at transcript level' + gtex_results += [ + [str(row['Name']), str(row['Description']), 'None', evid, typ, 'None', col, float(row[col]), source]] # write results filename = 'HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt' with open(self.temp_dir + '/' + filename, 'w') as out: - for x in hpa_results + gtex_results: - out.write(x[0] + '\t' + x[1] + '\t' + x[2] + '\t' + x[3] + '\t' + x[4] + '\t' + x[5] + '\n') + for x in tqdm(hpa_results + gtex_results): + out.write(x[0] + '\t' + x[1] + '\t' + x[2] + '\t' + x[3] + '\t' + x[4] + '\t' + x[5] + '\t' + + x[6] + '\t' + str(x[7]) + '\t' + x[8] + '\n') uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, filename) return None From 75ee52704741d68a5bacd571803dc4754fa4eef2 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 10 Dec 2021 14:52:53 -0700 Subject: [PATCH 027/112] updating processing criteria --- resources/resource_info.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 4d04ecc0..b505f33d 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -17,17 +17,17 @@ gene-rna|;NCBIGene;|entity-entity|RO_0002511|http://www.ncbi.nlm.nih.gov/gene/|h gobp-pathway|:;GO_;|class-entity|RO_0009501|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME'); pathway-gocc|:;;GO_|entity-class|RO_0002180|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME'); pathway-gomf|:;;GO_|entity-class|RO_0000085|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME'); -protein-anatomy|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;5|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;anatomy +protein-anatomy|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;5|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at protein level::4;==;anatomy protein-catalyst|;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;1|None|None|None|None -protein-cell|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;5|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;cell line +protein-cell|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;5|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at protein level::4;==;cell line protein-cofactor|;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;1|None|None|None|None protein-gobp|:;;GO_|class-class|RO_0000056|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606 protein-gocc|:;;GO_|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606 protein-gomf|:;;GO_|class-class|RO_0000085|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606 protein-pathway|;;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens protein-protein|9606.;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|2;>=;700|None -rna-anatomy|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy -rna-cell|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line +rna-anatomy|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at transcript level::4;==;anatomy +rna-cell|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at transcript level::4;==;cell line rna-protein|;;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding variant-disease|:;rs;|entity-class|RO_0003302|https://www.ncbi.nlm.nih.gov/snp/|http://purl.obolibrary.org/obo/|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc variant-gene|;rs;NCBIGene|entity-entity|RO_0002566|https://www.ncbi.nlm.nih.gov/snp/|http://www.ncbi.nlm.nih.gov/gene/|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc From 635121f86844056a42b0bb20f805da97b578938f Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 10 Dec 2021 15:45:33 -0700 Subject: [PATCH 028/112] refining processing pipeline --- notebooks/Data_Preparation.ipynb | 92 +++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/notebooks/Data_Preparation.ipynb b/notebooks/Data_Preparation.ipynb index af1fd624..eb024575 100644 --- a/notebooks/Data_Preparation.ipynb +++ b/notebooks/Data_Preparation.ipynb @@ -2546,8 +2546,8 @@ "# retrieve terms to map and write results\n", "with open(unprocessed_data_location + 'HPA_tissues.txt', 'w') as outfile:\n", " for x in tqdm(list(hpa.columns)):\n", - " if x.endswith('[NX]'):\n", - " outfile.write(x.split('RNA - ')[-1].split(' [NX]')[:-1][0] + '\\n')" + " if x.endswith('[nTPM]'):\n", + " outfile.write(x.split('RNA - ')[-1].split(' [nTPM]')[:-1][0] + '\\n')" ] }, { @@ -2650,22 +2650,53 @@ "source": [ "hpa_results = []\n", "for idx, row in tqdm(hpa.iterrows(), total=hpa.shape[0]):\n", - " ens, gene, uniprot, evid = str(row['Ensembl']), str(row['Gene']), str(row['Uniprot']), str(row['Evidence'])\n", - " if row['RNA tissue specific NX'] != 'None':\n", - " for x in row['RNA tissue specific NX'].split(';'):\n", - " hpa_results += [[ens, gene, uniprot, evid, 'anatomy', str(x.split(':')[0])]]\n", - " if row['RNA cell line specific NX'] != 'None':\n", - " for x in row['RNA cell line specific NX'].split(';'):\n", - " hpa_results += [[ens, gene, uniprot, evid, 'cell line', str(x.split(':')[0])]]\n", - " if row['RNA brain regional specific NX'] != 'None':\n", - " for x in row['RNA brain regional specific NX'].split(';'):\n", - " hpa_results += [[ens, gene, uniprot, evid, 'anatomy', str(x.split(':')[0])]]\n", - " if row['RNA blood cell specific NX'] != 'None':\n", - " for x in row['RNA blood cell specific NX'].split(';'):\n", - " hpa_results += [[ens, gene, uniprot, evid, 'anatomy', str(x.split(':')[0])]]\n", - " if row['RNA blood lineage specific NX'] != 'None':\n", - " for x in row['RNA blood lineage specific NX'].split(';'):\n", - " hpa_results += [[ens, gene, uniprot, evid, 'anatomy', str(x.split(':')[0])]]" + " ens = str(row['Ensembl']); gene = str(row['Gene']); uni = str(row['Uniprot'])\n", + " evid = str(row['Evidence']); sub = str(row['Subcellular location']); source = 'The Human Protein Atlas'\n", + " if row['RNA tissue specific nTPM'] != 'None':\n", + " row_val = row['RNA tissue specific nTPM']\n", + " if ';' in row_val:\n", + " for x in row_val.split(';'):\n", + " x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1])\n", + " hpa_results += [ [ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]]\n", + " else:\n", + " x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1])\n", + " hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]]\n", + " if row['RNA cell line specific nTPM'] != 'None':\n", + " row_val = row['RNA cell line specific nTPM']\n", + " if ';' in row_val:\n", + " for x in row_val.split(';'):\n", + " x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1])\n", + " hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]]\n", + " else:\n", + " x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1])\n", + " hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]]\n", + " if row['RNA brain regional specific nTPM'] != 'None':\n", + " row_val = row['RNA brain regional specific nTPM']\n", + " if ';' in row_val:\n", + " for x in row_val.split(';'):\n", + " x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1])\n", + " hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]]\n", + " else:\n", + " x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1])\n", + " hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]]\n", + " if row['RNA blood cell specific nTPM'] != 'None':\n", + " row_val = row['RNA blood cell specific nTPM']\n", + " if ';' in row_val:\n", + " for x in row_val.split(';'):\n", + " x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1])\n", + " hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]]\n", + " else:\n", + " x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1])\n", + " hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]]\n", + " if row['RNA blood lineage specific nTPM'] != 'None':\n", + " row_val = row['RNA blood lineage specific nTPM']\n", + " if ';' in row_val:\n", + " for x in row_val.split(';'):\n", + " x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1])\n", + " hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]]\n", + " else:\n", + " x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1])\n", + " hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]]" ] }, { @@ -2684,16 +2715,25 @@ "source": [ "# remove rows that contain protein coding genes already in the hpa data\n", "hpa_genes = list(hpa['Ensembl'].drop_duplicates(keep='first', inplace=False))\n", - "gtex = gtex.loc[gtex['Name'].apply(lambda x: x not in hpa_genes)]\n", - "\n", + "gtex = gtex.loc[gtex['Name'].apply(lambda x: x not in hpa_genes)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# loop over data and re-organize - only keep results with tpm >= 1 and if gene symbol is not a protein-coding gene\n", "gtex_results = []\n", + "source = 'Genotype-Tissue Expression (GTEx) Project'\n", "for idx, row in tqdm(gtex.iterrows(), total=gtex.shape[0]):\n", " for col in list(gtex.columns)[2:]:\n", " typ = 'cell line' if 'Cells' in col else 'anatomy'\n", - " if row[col] >= 1.0:\n", - " evidence = 'Evidence at transcript level'\n", - " gtex_results += [[str(row['Name']), str(row['Description']), 'None', evidence, typ, str(col)]]" + " evidence = 'Evidence at transcript level'\n", + " gtex_results += [[str(row['Name']), str(row['Description']), 'None', evidence, typ, 'None', col, float(row[col]), source]]\n", + " \n", + " " ] }, { @@ -2711,7 +2751,7 @@ "source": [ "with open(processed_data_location + 'HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt', 'w') as out:\n", " for x in tqdm(hpa_results + gtex_results):\n", - " out.write(x[0] + '\\t' + x[1] + '\\t' + x[2] + '\\t' + x[3] + '\\t' + x[4] + '\\t' + x[5] + '\\n')" + " out.write(x[0] + '\\t' + x[1] + '\\t' + x[2] + '\\t' + x[3] + '\\t' + x[4] + '\\t' + x[5] + '\\t' + x[6] + '\\t' + str(x[7]) + '\\t' + x[8] + '\\n')" ] }, { @@ -2723,7 +2763,9 @@ "# load data, return edge count, and preview it\n", "hpa_edges = pandas.read_csv(processed_data_location + 'HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt',\n", " header=None, low_memory=False, sep='\\t',\n", - " names=['Ensembl_IDs', 'Gene_Symbols', 'Uniprot_IDs', 'Evidence', 'Anatomy_Type', 'Anatomy'])\n", + " names=['Ensembl_IDs', 'Gene_Symbols', 'Uniprot_IDs', 'Evidence',\n", + " 'Anatomy_Type', 'Subcellular_Location', 'Anatomy', 'Expresison_Value',\n", + " 'Source'])\n", "\n", "print('There are {edge_count} edges'.format(edge_count=len(hpa_edges)))\n", "hpa_edges.head(n=5)" From f61103569114f826433e03408eb9376846519900 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 10 Dec 2021 16:10:32 -0700 Subject: [PATCH 029/112] protein-anatomy/cell and rna-anatomy/cell --- resources/pheknowlator_source_metadata.xlsx | Bin 30970 -> 33971 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 66dc5240dbefd1699cbddfba1dde6a5efd5803e5..7faec12fb745e94ff482bdd4b7a6dfab8797ae5c 100644 GIT binary patch delta 28083 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 G^#1|Fz^vK; delta 25064 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 UNJ!}ZUf_%l3*w?2cZJ^lKQ$HSO8@`> From c352c6ad4a5a328847feb0b4f7341a477a7aeda5 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 10 Dec 2021 16:21:10 -0700 Subject: [PATCH 030/112] fixing referenced columns --- resources/resource_info.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index b505f33d..a6aeb9e0 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -17,17 +17,17 @@ gene-rna|;NCBIGene;|entity-entity|RO_0002511|http://www.ncbi.nlm.nih.gov/gene/|h gobp-pathway|:;GO_;|class-entity|RO_0009501|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME'); pathway-gocc|:;;GO_|entity-class|RO_0002180|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME'); pathway-gomf|:;;GO_|entity-class|RO_0000085|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME'); -protein-anatomy|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;5|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at protein level::4;==;anatomy +protein-anatomy|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at protein level::4;==;anatomy protein-catalyst|;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;1|None|None|None|None -protein-cell|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;5|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at protein level::4;==;cell line +protein-cell|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at protein level::4;==;cell line protein-cofactor|;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;1|None|None|None|None protein-gobp|:;;GO_|class-class|RO_0000056|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606 protein-gocc|:;;GO_|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606 protein-gomf|:;;GO_|class-class|RO_0000085|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606 protein-pathway|;;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens protein-protein|9606.;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|2;>=;700|None -rna-anatomy|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at transcript level::4;==;anatomy -rna-cell|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at transcript level::4;==;cell line +rna-anatomy|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at transcript level::4;==;anatomy +rna-cell|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at transcript level::4;==;cell line rna-protein|;;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding variant-disease|:;rs;|entity-class|RO_0003302|https://www.ncbi.nlm.nih.gov/snp/|http://purl.obolibrary.org/obo/|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc variant-gene|;rs;NCBIGene|entity-entity|RO_0002566|https://www.ncbi.nlm.nih.gov/snp/|http://www.ncbi.nlm.nih.gov/gene/|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc From c15fcd67500e097d180adfe314ac73fa527ee504 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 10 Dec 2021 16:42:59 -0700 Subject: [PATCH 031/112] cleaning up file processing to add metadata --- builds/data_preprocessing.py | 16 ++++++++++------ notebooks/Data_Preparation.ipynb | 31 +++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/builds/data_preprocessing.py b/builds/data_preprocessing.py index 10f29af2..27f1697e 100755 --- a/builds/data_preprocessing.py +++ b/builds/data_preprocessing.py @@ -1333,14 +1333,18 @@ def processes_cofactor_catalyst_data(self) -> None: filename1, filename2 = 'UNIPROT_PROTEIN_COFACTOR.txt', 'UNIPROT_PROTEIN_CATALYST.txt' with open(self.temp_dir + '/' + filename1, 'w') as out1, open(self.temp_dir + '/' + filename2, 'w') as out2: for line in tqdm(data): - if 'CHEBI' in line.split('\t')[4]: # cofactors + status = line.split('\t')[1]; upt_id = line.split('\t')[0]; upt_entry = line.split('\t')[2] + pr_id = 'PR_' + line.split('\t')[3].strip(';') + # get cofactors + if 'CHEBI' in line.split('\t')[4]: for i in line.split('\t')[4].split(';'): chebi = i.split('[')[-1].replace(']', '').replace(':', '_') - out1.write('PR_' + line.split('\t')[3].strip(';') + '\t' + chebi + '\n') - if 'CHEBI' in line.split('\t')[5]: # catalysts - for j in line.split('\t')[5].split(';'): - chebi = j.split('[')[-1].replace(']', '').replace(':', '_') - out2.write('PR_' + line.split('\t')[3].strip(';') + '\t' + chebi + '\n') + out1.write(pr_id + '\t' + chebi + '\t' + status + '\t' + upt_id + '\t' + upt_entry + '\n') + # get catalysts + if 'CHEBI' in line.split('\t')[5]: + for i in line.strip('\n').split('\t')[5].split(';'): + chebi = i.split('[')[-1].replace(']', '').replace(':', '_') + out2.write(pr_id + '\t' + chebi + '\t' + status + '\t' + upt_id + '\t' + upt_entry + '\n') # push data to gsc bucket uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, filename1) uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, filename2) diff --git a/notebooks/Data_Preparation.ipynb b/notebooks/Data_Preparation.ipynb index eb024575..750ee0ba 100644 --- a/notebooks/Data_Preparation.ipynb +++ b/notebooks/Data_Preparation.ipynb @@ -3832,22 +3832,33 @@ "if not os.path.exists(unprocessed_data_location + 'uniprot-cofactor-catalyst.tab'):\n", " data_downloader(url, unprocessed_data_location, 'uniprot-cofactor-catalyst.tab')\n", "\n", - "# upload datta\n", - "data = open(unprocessed_data_location + 'uniprot-cofactor-catalyst.tab').readlines()\n", - "\n", + "# upload data\n", + "data = open(unprocessed_data_location + 'uniprot-cofactor-catalyst.tab').readlines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# reformat data and write it out\n", "with open(processed_data_location + 'UNIPROT_PROTEIN_COFACTOR.txt', 'w') as outfile1, open(processed_data_location + 'UNIPROT_PROTEIN_CATALYST.txt', 'w') as outfile2:\n", " for line in tqdm(data):\n", + " status = line.split('\\t')[1]; upt_id = line.split('\\t')[0]; upt_entry = line.split('\\t')[2]\n", + " pr_id = 'PR_' + line.split('\\t')[3].strip(';')\n", " # get cofactors\n", " if 'CHEBI' in line.split('\\t')[4]: \n", " for i in line.split('\\t')[4].split(';'):\n", " chebi = i.split('[')[-1].replace(']', '').replace(':', '_')\n", - " outfile1.write('PR_' + line.split('\\t')[3].strip(';') + '\\t' + chebi + '\\n')\n", + " outfile1.write(pr_id + '\\t' + chebi + '\\t' + status + '\\t' + upt_id + '\\t' + upt_entry + '\\n')\n", " # get catalysts\n", " if 'CHEBI' in line.split('\\t')[5]: \n", - " for i in line.split('\\t')[5].split(';'):\n", + " for i in line.strip('\\n').split('\\t')[5].split(';'):\n", " chebi = i.split('[')[-1].replace(']', '').replace(':', '_')\n", - " outfile2.write('PR_' + line.split('\\t')[3].strip(';') + '\\t' + chebi + '\\n')" + " outfile2.write(pr_id + '\\t' + chebi + '\\t' + status + '\\t' + upt_id + '\\t' + upt_entry + '\\n')\n", + " \n", + " " ] }, { @@ -3866,7 +3877,9 @@ "outputs": [], "source": [ "# load data, print row count, and preview it\n", - "pcp1_data = pandas.read_csv(processed_data_location + 'UNIPROT_PROTEIN_COFACTOR.txt', header=None, names=['Protein_Ontology_IDs', 'CHEBI_IDs'], delimiter='\\t')\n", + "pcp1_data = pandas.read_csv(processed_data_location + 'UNIPROT_PROTEIN_COFACTOR.txt', header=None,\n", + " names=['Protein_Ontology_IDs', 'CHEBI_IDs', 'Status', 'Uniprot_ID', 'Uniprot_Entry_name'],\n", + " delimiter='\\t')\n", "\n", "print('There are {edge_count} protein-cofactor edges'.format(edge_count=len(pcp1_data)))\n", "pcp1_data.head(n=5)" @@ -3889,7 +3902,9 @@ "outputs": [], "source": [ "# load data, print row count, and preview it\n", - "pcp2_data = pandas.read_csv(processed_data_location + 'UNIPROT_PROTEIN_CATALYST.txt', header=None, names=['Protein_Ontology_IDs', 'CHEBI_IDs'], delimiter='\\t')\n", + "pcp2_data = pandas.read_csv(processed_data_location + 'UNIPROT_PROTEIN_CATALYST.txt', header=None,\n", + " names=['Protein_Ontology_IDs', 'CHEBI_IDs', 'Status', 'Uniprot_ID', 'Uniprot_Entry_name'],\n", + " delimiter='\\t')\n", "\n", "print('There are {edge_count} protein-catalyst edges'.format(edge_count=len(pcp2_data)))\n", "pcp2_data.head(n=5)" From 028ebe53b8282f304eed1c29f6db0a50faba7bbe Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 10 Dec 2021 16:55:26 -0700 Subject: [PATCH 032/112] protein-catalyst/cofactor --- resources/pheknowlator_source_metadata.xlsx | Bin 33971 -> 34941 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 7faec12fb745e94ff482bdd4b7a6dfab8797ae5c..6fd20f79e92f0a5a3c9f065070c9e8c212e40cd5 100644 GIT binary patch delta 27609 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 delta 26654 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 XoIv3d|HFV3h<+5ra3#SPef0kT&RF95 From dda44719242177123eb12b8e7d1d7d4351191cfd Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 10 Dec 2021 16:58:33 -0700 Subject: [PATCH 033/112] protein-catalyst/cofactor --- resources/pheknowlator_source_metadata.xlsx | Bin 34941 -> 34943 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 6fd20f79e92f0a5a3c9f065070c9e8c212e40cd5..574e48de3422c4c92a4f98dd07651f32d99d58e5 100644 GIT binary patch delta 1646 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 delta 1637 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 From 8672c82454cc68836c6dceba830cdf9a60e5295e Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 13 Dec 2021 17:15:43 -0700 Subject: [PATCH 034/112] light reformatting and re-org --- resources/resource_info.txt | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index a6aeb9e0..4ce96351 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -7,27 +7,27 @@ chemical-pathway|;CHEBI_;|class-entity|RO_0000056|http://purl.obolibrary.org/obo chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); -disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|2;!=;NOT|None -gene-disease|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|6;!=;group|None +disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|2;!=;NOT +gene-disease|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|6;!=;group gene-gene|;NCBIGene;NCBIGene|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/OTHER_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None gene-pathway|:;NCBIGene;|entity-entity|RO_0000056|http://www.ncbi.nlm.nih.gov/gene/|https://reactome.org/content/detail/|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); -gene-phenotype|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|6;!=;group|None +gene-phenotype|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|6;!=;group gene-protein|;NCBIGene;|entity-class|RO_0002205|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding gene-rna|;NCBIGene;|entity-entity|RO_0002511|http://www.ncbi.nlm.nih.gov/gene/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;1|None|None|None -gobp-pathway|:;GO_;|class-entity|RO_0009501|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME'); -pathway-gocc|:;;GO_|entity-class|RO_0002180|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME'); -pathway-gomf|:;;GO_|entity-class|RO_0000085|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME'); -protein-anatomy|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at protein level::4;==;anatomy +gobp-pathway|:;GO_;|class-entity|RO_0009501|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +pathway-gocc|:;;GO_|entity-class|RO_0002180|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +pathway-gomf|:;;GO_|entity-class|RO_0000085|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +protein-anatomy|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;anatomy protein-catalyst|;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;1|None|None|None|None -protein-cell|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at protein level::4;==;cell line +protein-cell|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;cell line protein-cofactor|;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;1|None|None|None|None -protein-gobp|:;;GO_|class-class|RO_0000056|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606 -protein-gocc|:;;GO_|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606 -protein-gomf|:;;GO_|class-class|RO_0000085|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606 +protein-gobp|:;;GO_|class-class|RO_0000056|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-gocc|:;;GO_|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-gomf|:;;GO_|class-class|RO_0000085|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein protein-pathway|;;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens protein-protein|9606.;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|2;>=;700|None -rna-anatomy|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at transcript level::4;==;anatomy -rna-cell|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|7;>=;1.0|3;==;Evidence at transcript level::4;==;cell line +rna-anatomy|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy +rna-cell|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line rna-protein|;;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding variant-disease|:;rs;|entity-class|RO_0003302|https://www.ncbi.nlm.nih.gov/snp/|http://purl.obolibrary.org/obo/|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc variant-gene|;rs;NCBIGene|entity-entity|RO_0002566|https://www.ncbi.nlm.nih.gov/snp/|http://www.ncbi.nlm.nih.gov/gene/|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc From a879a9dc6a52c0f3d02b15da3105bf39c7079f3c Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 13 Dec 2021 17:15:55 -0700 Subject: [PATCH 035/112] protein-gobp/gocc/gomf --- resources/pheknowlator_source_metadata.xlsx | Bin 34943 -> 38070 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 574e48de3422c4c92a4f98dd07651f32d99d58e5..ecb6b00d898c39b8af897817af49160c56750fb1 100644 GIT binary patch delta 32263 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 STo~L4h)N*&i<GEmk^cwPwa^Rz delta 29145 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 V%>SMRzAXmCMZ{AenFigr{y)ld9Yp{D From 160ff8ab8ec5d9f5bdcd2e3cf8a8557054b8e7e8 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 13 Dec 2021 17:27:32 -0700 Subject: [PATCH 036/112] protein-pathway --- resources/pheknowlator_source_metadata.xlsx | Bin 38070 -> 38696 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index ecb6b00d898c39b8af897817af49160c56750fb1..60933fc604417cef5e6db9692d6a9bce49ad6688 100644 GIT binary patch delta 32271 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 delta 31704 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 From 4618728cf94128f2fe9c3a964f14a92e298094af Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 13 Dec 2021 17:36:18 -0700 Subject: [PATCH 037/112] protein-protein --- resources/pheknowlator_source_metadata.xlsx | Bin 38696 -> 39145 bytes resources/resource_info.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 60933fc604417cef5e6db9692d6a9bce49ad6688..2af158d09ccf16188f09df8d20ca93d51fc513e7 100644 GIT binary patch delta 29177 zcmYKFV_>9B*98j4wl%RQwrz7_bK;5Jv2EK{$95*h#7Spj+nSU6dB5+RKlP)p)m6Lp zT5Dg`Rn--B5GRcg4K(oZ55WX~b0ERMMw40K@c_1t#XMLay$heP!yYzM-=R3arLH(| zo>bVdUyS_*$E*pOCRi+~I_$gR^lHhi`LQ-56*=%Y{Wr&Fmh;Gn%ic^teT{v<idv{y zG5A(2C8pl;@JZ4kv~2$Sp!@=ylGP#N#WXW+-#_<WoZFvt!^z-a-T9e7HG>cpqoo$A zk4(TZZFsKScp7cSZ|qZkS=qIfl&V(IID6O%s+erO5ShFnvZ^R-4(yRRwYpZ)BrXZ? zk6ufIK55%%^&MUE{ID5%i4E}KBu<5%n1Zr-ocTXdh_Y$O<J-odRheg)>me?rnNQgU zxBH4TY_<C$H7l^#){^*pGeVPhGU)6*q8vcjKACKOX5HVwR3xDZdLCnFN8{B`3l5}Z zf*uUfTcOB9l6Kz15w8&f?>=CiXy+y2Pq$KnMQfS*rX)JunJIXMkt~1h1aVx{nm_|2 zl358{SbmU9aS1j)0T7Jh>Mt1!Tv=Q_#Q<J<Y*7-XB<2A2vs3w*mLRt&4G=7xunllu zA1I3u3;hXE%aTM+KKU6|v5sfX&OZM{JWMfC6?L5+#DQE1FI|bXrDiurl9U4J)cxIt zjKEdB+*&COb*|M(xzD*V50i@0yahLy!8(4MH;brk7I4i$=URkjZc}aP^Uq~jBqipt zw?}m$;H}yf1=vL~t0H?X-(h?I-2g!Ul!(hV7+)WleURR|r`v%$`s2{M6+7&57{QbV zDS2*4hwBC0)Eo^v2&rm2Q$l=J0hQ=ii<=_X+c!ID$nPNQ?)9$t(t2U!w;mGR1|gIy ze1j)P_XTycyM9l)WzvBG@Ct9<DSOnivtLhvsa1KI4rqMgKw&z)KO$?thExC^@va=e z%ohSa**wbhKx_r|4FomRnG=CrU3HSZ;rK0CqEQN}`?y8VTZpzBo9jkB>y9AEExhzm z%IvV1dy0L{NMPC1fPM-|&|f$KD1Nh%r+6@SoH^N7wj!m*3k5f*tqQOD(eD`<fmr8j z>6kvJI_`(BX}C(?zTfMQ$#nykCf;S!EF0^iD?x1Y|9&mU^<Z5X8D&{0mgvf;2@J?z zrTt(%8SpFihVlg^$9}N18WbEK@U#gC0Bd<r1SXYTR`Pc^Cf?s;YtoGTZc7m7pB?3v zz>up@1AY;ou!3rdajGu{>&i$7I6D>HwmtrRw)OL?faWlR@7^#WlN||HqR%kRj@EW> z$Vg)E<89jh%<UbMwbcI|0f9B|BLR^RXiZygGX%lFR#TFT@#(R&dubw*IiMtTRnc@y ze^xE~=@X76@8DwrM*ZJ=kBF??T@pCGch}+mU}Zp|OLd!6VNI9ygZgTvc%?=c%1VGX zo`Lb3JK1OPB$hs<B-3dw(E)QA5RSF><tEmvY4SD@e{NwuD^t>z22~uz7HyQ|`(&3* zh$MArE3KWRGfrLbs;Z#&<VW0-xLbp*4b>4}r+>6q7Rb5-P?Xd@+HIv3CUDSfRS|ve zRbnW!x9tf^PdZ^hut{oRC<H>RzR7>_g#R5<(q;UR(&PejUj6BB3&2nk`>j;c_BlJ4 zz_Ii$+YTXAy!#|eu0`|X9Z7y0eo~1gok?&jvZ)_Xv@zl-q?AU{B(tl^A)Bl!WRy71 z7QHY+KY3>d@O{HInw0~`cCSk~)C@c__fAiM3SVIdi3&r3vT<j}7akz%d7Fup$nZXX z$O*BWMoGi9Jp6ng0h({kc5)ehe>isXXkZOW)?0US^|da<7nrDeWUoIDT4}H)K1v|? zUE{g?)X>mxYf+oz5ZXtNQ$}Cwe>ZCTkd%Wc?bp)|2zFi7#IS1kNPjwoHRcfD^?MO$ zH+-pftDnyz=v8@8R$XU=gQ=&gKdx&v`tym2n_b)dS8m7KQ<pe>lcin2m@6Vzs?5fC zkU43v2dd!dWMIrMJ_pJRLmED^K4+wO@&t;amEG8vr<KMHNhZ-sga~h<6iMYAAI(Qf zpf@EE@Q4%<97oHu0#$PdLHd}DiD9loU}1FxZgYx`20J{`LG*+aAxP1KFfPfqL$#gC zA%Ck?`K+vC0D6ggh6Mxr_Yds9ro_+I(V#8qz=-v|lF)k7`s!|uR}_OZDJL1sFWEKc zWWblr^<a<C#Ey<);$#ybylI|TGq!8SZUjIIrpM`6A=3eBQ=U~Hfo6`<vp3Q=9DBXJ z-}AMUjr#Ho`@BCt9{;X=Tun}{)cgCqygqdMe4Jk1-A(S5U(G)K)0fMc%U^tayqtXc z*U2jk$|ccX8vz}zfPCHBj-TCCZy(Nv&a~>@*HS-krbo1l-fyP0j5ZU8Q&TfahXKIU zRfp2?SnQ4z49A*bQ8`~LDz0QKUY$g<r02nbw1#X(_r+H5KWay3E7{0HHpLAH<aN04 zihqXANE@v%2<QvMKPVkT#wU~C)`WcTE@$IOh(Fyw7UNeypRbNucYtp1kIM%@cW2(e zk7{Ni2H~@ZdhRQS%lsJ+X2;KVfH}awo&US1XxAw&4>bCd<7>CLl*rDR$bwmO<dxdk z-kT(mcVP*PRl<(b<<fXGU(o`$*iW!oz^r4N^3hYhkzlT{@5k%(;Uu>My&$M-;#OC* zD{{_(r$t!6mq(bfqPNG#cOiM~8wH>bogC*Jtl{nF;Pd;-o$#l>|G%N>W`e8&vRETD z^yh<Kxc4-WCS&$=VaHP9b0|h!T;9o#-GXv&v`oYDMtsEg0K37|(an>0C;zv4<$s>< z6#<y9@25gPcY-3on3%aCc%B-o1NBa2cEsRGJeyA!fY|dry^-YbyahjWGOhqM<5D_J z>S%wB?QXrv`<-AsIPv7a!AsGi8=D6oAm0PAS&bzUp~7mLRjK#+NsH`$CS;@!(DiP< zAXNAHR_^s~DlXXJ;WvHC*UMCx3e<+=sfvVi<%vNN%!=#(Mco?3Y^kQvMf;1E5_9wm z>~CqDG!MQqeP#bos+OA=jGyLkk@Qr1UhlWl&DX?B;DFod?q|lC8HbkKT;>JK8_9nT ztjF1FOek!EDISLJPe9i}tt1B{hAMDJ{P5OhS$TpOHD)g4Y>N=LRh=biNP}Z@)Z$#} zh>-e{g67L!ctdUUY3pLsPe^P2?-p6cY3B?!zjyGwH;1f0-n9jxB9T04gr2_}#^<cU zNXwJO<N_3JZt<{yuVcpZGevm>VB*DsG48~LjJ-(&5d(rp--{cY8+BtlsWM|o=V~q( zzGlt{@5Z`LMcw89vp52hL`y@%tfiNc^O0j-nGGMOdQDrm#LEmuUd0^$LSPWum+Fai zP@<~zH7cZvOsne#puv7iT{%$@q02MgsT}6Q4Det#no(}^V19~6<r}r>GJQ;__)bo) z4ktE6vDB}1+d%CuC#i{M-@jAA^AB0gY}voC{J3794`6X~k<-`2`T5%-=%Wejt&Q0C zo0P-{yEspn;Oe%BA^ARj5V|2NX8rXH1b&Q)h9oEoZwRpk?>ZEzmDK-sxh?60P$C>T z4p2zP8r2*#{3|5tdA?BU7b&DBQSJBu7{0}=8mSGZ$=k_EejDo3Y`%Z{d^3sb+zyU8 zltU;xLpJ*7?swVEiVA`xXtuCHs!S-Ng_;Y-gtZehLCPV~HEj1>E((c3EwutD*lvQX zQX8O`Q?fZ5?T_ZhR*}0OI=z@H471xYXn-IK&}x`9c5+{o8n!pIL<~@o4IgWOp#Zp5 z!mdd8tKuX)JP9b-^XD$sJOrTE&>BtA^r@0e1!t(7OA3Z=W}isVvm>9G1l($0RLUN4 zb6Iw`isL`HQ#$Qe5BjF3w<H`aWQ+@K+eJjI9WTWDJ{9}uO>nH2RS*lcVA_vs187)V zJrE4Wn3hz;ClXgT#lo$r{a3iMpl>AG)S|7Shrm$JvdmREAs<-rC#2TeUKJMEA+dqQ zvEJNXnszE|5a0J*Rh7|@u=>xcebPif`1I(yF21dOFcOJ^q%FUg2-m=f`yL;9g0{w9 ztH@|4X$c=JjR>Qyy&0oFpKNBYI*!^tALDdw5%{E+-O+HU*(yj-$gdN*d5MA%3ecCc zHE}$qeB^P1Pw%3D;r2sWF6r-0%ygYG_9Mknt1lIPa7hu&0v8Oc!SS1<th$u0Mle78 zn=6`aK8h(73+U6ow@dg8a<*XXu$^}Tj&jWnkvB$qbvO?KjR8tR5<+&GIv~aX6l8<r zAdxlwier28)4~t}L5wKvguF`$1CWL;)L0G%5l)4rNnxfDJE^T*lqb+#W7##RlPO!z zRld|sjrE2u_V;_{T!y#94!zt>D5%cKnE9&-Q<vEq!FH%@t8qHy&GsWFdXwPc`_UfP z5BU(Iw|a}fJ;?XC|HKYp#QBp5lQUT7MEy+awuj9$AK_PoUW<+5I=YGgkSFE(>LWz- zoG~~;QWLfAq6I`L=tb`#K_hauxLo6uhN`eJ=o0sq`21ym-}bm?iJBh|e~~D_faeI| zyO=gWUMflN&@Q51mwr^(`58=D>fPj(>z7QC5pEzMC|m)L$AW?-eO(aK*2DE=`)H<z zHj_Hso>~Y?uLtu~UOvD`N%-UFCBE6;3(YSnFN1QZ?YykxnrZF;F8VrkKkpm1W+95J zh*<68gF{jt2WzS2l<bzgj8b0F0$|hsy~YSZa*Mqo@LJsD?a4XB<5>;?=>8mN1HWYH zC!P@U40Zv>?X*-!lAp$HH+u#6bc7-~E)PVN_$C)G5XTZOPY7@;C^{{G>^50$(@4$} zIOZEfA?h`9%>VTAe|fu@+;aT)aLeo_jO?~p0Bed0z3s+3h~rDe`D_;=0`8&++}}}$ zxe5z;-oeb;Q0FRmI@_DPkPaX|E)!)QznAD{l9marFPUpDgbF6Uez)r37pda)7IPu& zsov=N_cmKTHVclL>_@)@KHIGJof91Ja@fwm5iM-Dy~nrji@z)9`G-X6gELR|xGrKK z?J*pc!fseANz|XdO6;5d%j>7pLb7y0#SiU`=QF)I5*T~J)gYws3zbz8p|}P(BiSM; zNzSE&h@0?a6*0vhw#7f5!c}QFd?_h*zCRC*q4WLyPWLP_EIRF2Ma7+Q|LKGQ_<apO zZU0>L$#NPj%FIDK_r0BB44vx>cRx&5x>II*AMn+iET`T#b=g?O20Fcm@KUxZY8j~l zt!s%m3cIgTbR5_pEQLBkegDZ)d3YSiu1GBNyG44L@>gS-a86dm^AIJ4z0^;zI28m( zSnQAi*!ntuk>Tf|k}PrBDj=ZTF|1(+wVc`pEli3kq*Rqhj4p2fpt)FCW0;J?@e=}n z?=NL$H^ll**iCq%<sNEsLO|(0Dv7jpP<@G18uE8MKt@&MeEE-qY|3Pl#;jQ37!F&| z{jeq?;F)I--j`|?{`s?Ez3RU@2oWW5ZXH=dIz(<QHbElPIf-`p4q)INKAh+_;~R6u zS|O`KVLfeNtt<3Ms>DH2Ad)_gsHddUVW=`eMq$fD-YuaqW30l);r<jMc8*YeLb7`- zQJ*IxE<_*0T8aR<V@KOm1V#k7MJPgv+c*?2J4NshVk{M{mVD<KzU)8l{~lYIM&w>u z6GWQAnmw#TA1k2Q2tYRd!x*+TXU2j6kA>(T$!{$XLLQ2;%B4QHpHN!_7lc2`JClVM zCD@3-6<B@AdR`@o4)q<OZo-vykXejT5NG2iN<(?rrzJV36fa85%ZGt$%X}3(+T6O; zc8_PZ^UoS_Pw0|LYcOshC#Q96^A{>b@CK2KH2ae6JSvn8z_9@g6flTMh*Oal$A5Pf zYO@!j2jvW^`Gm{zPo6-Z;<xdxn9XF}WEX3!NHukRXK%SjFGiEkM;65c@p7z$j1AMN z2xjN}21Kj%{uxthqUVmYpr49H$N8(|6q4ROlGUf#_o9h~IVOj770>2ez8IG`N6MdE zoRYq2P19-)KrqB9vM^f{a5(wQV-qoqc-z!eCh*V7Z7E#2#nPaoI~jpX&-7({dTWLh zZmdD^ak@JroYKM6`zI!;X2?SIej~T9I>e;=ET&G>o1`pg5TlZ)B3}%v#E>aU7))fR z%I5F3!~|2TvMp7AsdI!wug9n=)31GBE8RZfDoWf7zzZp?5rPvf0e@ar#Uu<7^CBD# zb42d6fi*+JpiT2VYlPZi_2H6E{9Q$x6tmg70?S%fLRU<i`c$O7`Y?&SG)m4%r^4{E z9DLFj$pVKZuJcV2jf54JjasgEy4;n%%6_LAy759!J~ni-gp$%;{+9^Kd`gC=o(R7F zD_;sN0K>GoQd-eY1mWNUeC@IJ1XIFedWo70-r|==+^|GhM$Lan?``fgJ@t`w*iDHG zQw@lN*Ky}Mt%ur5M2NhFvU`p+EdgpA+-s;)vYq)O4l$S0TE;zrBzN$5O$Wa-jrc3f zjd@H>e2_OoqsP=aWBb$(zn4E)BQ{-3GNS;?5k)mSa;_Pf&~z&Uk{rhi^;o7}pqKCR zhv3U{vZsIRrCzav-l{lm*JCA3K9kSGwwMCe489RV^`O#u#AHU#>0Ehe2kWZj$l&W3 zNp5H_=V?(WGY3^G0wOFAbv~?*pf}bb+HvF@EK+g!^8HLq?|ynIo_B0}6ion9PZ+@Q z+hX=g+{1{2>xJIi)Y_Rz30znJI+QqvWaPxih+(176e|@SjG6Ly$bj}!tt$7qG1NeE z(RcP?71JNT0?8xf@vv0EMqZbxgV;u3Am1-4^+9cE>vF^<$Dw%9RLqvN%r7aJ$>pRP z^wo?Jw3;0cwaFU9(X~^1-g0ezwo3qPc4FBIN02vdt!G1)O*4?9(X)!C40uy(@BB-% zQIK?_*G)O@X4MYV`=F9_1HX^2YyoVZl^OSzri0`mOeN3SwJP3f3zPjlJ<1^aaG{54 zftqg$<IsB_ShML4>ZEXcQe2qn##5PnU6$!^cUBhb+H511gAy7U-Y^Aumjm+oqfK+X zk3ClFiW64d=apf2eew9OD?M|E7}}+O?RjtW9j#kY8RXdBy|$<)=bZ7DE<Jx9<!^sD ze>(Z|32&Zwkjz<SmUk8<82g<6)p+=hB&lYa=Q#6NQ3Ku~XvKMPzKdJJvz5G#7){c{ z(<6chidB!tRVYXY8<^{rXwXZLTi)s+ij+KPIhaBlLzA_vHlACj&{<Rx{)Wp3Y_MrP zguk$d>ht0I*Ej2ccKSrpbh*w&Hm)r64e<&_Q5^rbI3w@@HOT`KPITYA9ApuD3LPSt zh{**9%1hQ{XNBE+dqaIXqDhWrr-ON1>v?)MP3~r=LA)fn5)$6(-4qtGZoXts04L*4 zZ?T1c|Ho_Zc-8CsuRi<kZtG<L+wgt5NB1o?Ujw9+oX;5z*c8s65$=6?*qYuT;rJt& zZ$ZrfFI%ODU~szmH@6Q$6?W!;7S;ptRz+RRVa!~>ab&cFCnz*s5Vo=UdW;)?FrP8x zkv5%^=$;W{a3L_RkZ4a`VVsc^t1+g53|`7UAqkCOQuKAC!ahVU?YTbi`sMOwVpu5Y zcD<Dy{ui1Rd0L)WEsS_$e1?`@03IYxjU$)@+B;8%3u}o{0PT`T+z@y2Bo`CBW4^y< zJD{WQPcj5I6J!?vESa5~7V!N1_elBa$_=HQ?xemhj*FhnC}s6VrS*o6`VxmHC<3w{ zJwy`4GC2Akk%)hg9B!-)wNDSvCx~JRmX}EW@=0azcBj8`2TtzA-h`6Qq!N`{$gVkM zjCvydl!I+HK0p7QH8gL3zfmyKG?EtefF@rg=vMnje4Ok4Q}0Ap6JQALbnO^U!{MAo z7TzP_pgSdRN(JHu-W)C7;^A0EdffpQHQH!S3a7$~msqWCVp-N&OB+d2nzs0{X(HFf z|4gAnOC4)Z%@`%<I^w`n7MD=i?{DH32Jy{LO5Cfp5<-_A1tQ1vK?=^eaG}Y#@j2c| zmB>BhQY;VYz`<JcUcedJBtb&(^?2#uuP+C8QwZ#mIyeg~DI%I!D~M_DdWyr3Z|RVN zkwy3hs3MjyA&*7Bu1yqaXmki$k%kZ{+tBC4k&&U8X70UAZB_RS<O~U1H53u;d{IEw zNW^?%x~HPSkYOZ<!Z>y|p&^u*AkmP}>P#T_gV?5Wah^6Fz%7CnBFiu)--#FWtxbB0 zrN*f{uVwFspDKka?&!|5o+O4;Kgc;Al2){P*txqTT67JaIZ#C`E}YLceRQq_s%iKp zOf6oSm889aiIfODKtdF8=tiyg7feaTDsQzs5h|5j8_zmFr9~WfN|Y}|$GE!AmfVo* ze%KyWWlV7mU^Sj%*Yg|iyz-6Dz7xZ*?!x_pfk1V6N;y)8a0&%u2cA4dlf0~&t)ES4 z1kT-Yl&2^oXiJH-X*}EhWJuLo(nj*dGT_-ZM9e?-DVBL+WFwFz3pR&u3Y^)sWaZj} zCBKF(@QuR%{gQ<{S@lbR)O;vMHe$f6BmTfsrh{4!;BYUYsnoUaDHQnhS}0a06u&op zPTXx(sfB3)i;ML<ILxbxax-CfNMnFRiiSQf+UeJe-=xefu5=WB2XZ92bOe1|HOp7o zpgacoZ_r}GPZ*?QMBaIIuM8;&1UZ7MNnO9zA)-Nu&uk-${=SH58Ho2<Sc{`kquR-o z%ezqlaNW^SW8z+~9*6f#OJYklEHY_-y>gv?@Ma_yz<S%X<4?3y<9G0Y&cfyR<WoHi z=3-h>jA7U@WCHQ=OYuYkcrx%a4bO6&*KZDEBs8Q(SH6`!_W$u!%}I<-?<ThFC-ruY z?}F1`RjAk)ryWKEKAa2Do`Pp22H>Vx?az4u=o5l`Au<OCV(juQIw!L51X_tR5`T;< zAai@KnlSQGgtH|o(_+5}&{er$kDQ))NxNTV*U$|?p03RYhfwF13EJRx9gkQ)sytr9 zm!N)`#^64VVH+iHNA%7Ux57#q6^%`J@5?mBR^#~bvIC(*!Y9MI>R8pUM<V0ikg|Y? z*gWEYL=VPnN9Qtf&qo^zc_FjWJ>Ea#y!tx;#FJH@yl4>DET~zgECs}Pg3FQl53|Qo z%Jae`a;Jg^HQQ5!t5tj794^pNT-&?r-wSLhp>`s(R*%7d$1+EH8><D3Fu@r$33!Li zkOwMktIG{ci*TGn!-0F&R3o1T&O-w9?LXK%=Z9LVfmSzcE3=gABDl7*KfInD?pKk$ z-z*8gMwd{uy9YaPzXu~rW}$dg_g6fr37eJcA#U9F8)J652BTSz%WE|{V@P*v33O)h z7D2uL3K4a&PHHwGI%m=04mg%`1O2mg;28WU3`w<lqMfE>Gmg00*RqZJ1mHkbnPjU> zH+Re~S5G3i@8wJ^-%&~EulyDhODL!sp~$3%8bkb@V`HVI!=2YVjLlM+*Mf^g@RYkx z&Q!CnoFel<aXIBL_(RP~Ovw4Pe!_PjOI!_X)9CO{@B0APHOz5wdtNQ+CL37gZiW7x zlMbT*?*bA@VfYIK!~;&@H^4@9{iTp_pMd}C<;}_Bg){fT!*{^$R%YOJ_|=Dd-aqk= z894<`#>4bpz)_gab-)XHgAQn>XH<=PGvAe!a&I6{tUdqonm8>QPKfu$S!QIb+w19Q z3>6j7^?5RO<_P#`D@{L1_)TUY@qFOxb5y`r{-jq&0?JO_St?(b0%W`;iZPEMv@=6n zx-DFsUL7hp?^z-EH~K(OZD{^B*?LtxR}A%X<VsCudd$Ax)VR)E`h2D~c|W_zk4F{W zJVqob=>=!@qcHmYNM*y3lFA?RjoTF}W<bz!0IuZmFoTC`#xnBWH2MA{l%+WAX5;AS z4Iq>QGRjPMt_X!m0u(ko7o-sh=7)NJn95tdLMLfZ2ay@4f|(Nf*k(1Eb#V`@W|UOD z-*I~bUmc<}q2*4;oEO&&<yRXfn{Duk$<b1N>&9_6FgmnTYt}eU(U8HQsW`j~7WgiE z7=9BFO~sS2@C<uAr7H#cWJlE63?SCO`DiqPD=!W2H2$vb1Hh)tddKj3X1H@|gsqAx zj9=Vllr9#rcLmBO4%3d7_G3Y2A3XkWSQr7b$ytaDTRk|)^X=UoL36AjQ_Acp7nhsf zvWnRF3mz`G+Rse5TZrr{SA0R7YbuUumk)U!h{e)wmz>)=Z>n~EDvQuZ;EuQHjyEs> zH}imne5o>e31E@hMElD!%CY@G7E@9)ps3~rHyH&TTy4TYs6U5F2th;E*xba7zgf7y zg?Z4QCIbV})Qy|9g2Yd5rpW|@<bY#l9E1}vAj#MeL2yBufF)gN=M<jgs+=;WTLk5P z6*~}n(o=0RK{*U|3XcqSaA$Z^q<-E-BvD#XJ3e@E2>1mGwUSf2$TOX+)>W7`Kt={5 z>+;DA!4nXa_E14>IdHkg5{DFObG<iyYb+?{A6%S6rO@vvH=H`~?)NQBG3n)+b=NQ| zfi-uEpX{m^+Pg7zY8%}lzB|b?<J(%aaH>>x6B8Bln_4-lHvH!TCKj0F5`4h8gatK2 zpr2jf11Kuu<GfRCX&p%IL|8$Rkr~uBZ6{Cl_*X$)pL@raphSrLS4Dl$8m~_A_*<Ao zXS+U2JHcqw01yiXPhUjoH%xkRvOi^TFmpTM!EeLu$~pr5s^23C1B#}010_u`L$Ti~ z**9rNy!BYyb6W3<vGjjMVnonr2XA1`7f^1zR{+H5P5S~&+EvDLrYPuGzcJq}XPCPQ z;Xk};GPbV<{Z-RF;~Rn&>)%<AfT^&;XF3h^!=RPzn5S!S(kfp4^u1Dn2W~dnOgOm- zFhpvW<E1UYaLbp%m(izfcmFaq>do{cKN{J&Y#DF^#OqC8_Wk?(gG-VItLKfxblRZ< zDgq?jLnD4YV2mxy57zgQnhv7Qv>ek#ptV49NHbjxr~awKpp77xyt%3-l?%oa216AC z9Zcwd0c00gN<!jJ`%?>*!iK~x&x!|qbR5!X?W8IUmQ*;+6vqGdzHydV@&RBat*n`G z5)T%lOsNgxY*&a0gx76A!fiFib4WQg1+-rx{F!Sh?ePo5P|egu<Kb8t=gAGl6A|g) zYL|KOt&H=`D@wtWNDnQ(Zk8F}ax{;SVhJPUsMJ&>5fK#|-LWKtiSOC9Lt;7)^a>EM z)`CfwCV6cvP!}CcU*bKgv;4i$V;&)|bRk8<)XNm{GpK(UPilW*5!HyRlZmm7A5cNO z;LM+{HsX<cY3Hi0d-=6F_PBMj!VT#4xjVX2w!-D{@%4NHwUG#);W?h-llU7KHcy!j z#UWLIEJAeJDOUXD1K*|^YhEZ+mP9{)yv9<M#eTqP0G}zIJr-$c>>(a5jsTmv!v62? zH;*S<0HN)|^fPr^WaE?S5!&x|0LM_#hV3A2HVlpz%BzQYMOO)k>Z?6AdnE0uP7G~V zXtona5n0eg;ZpR>lWMxpJLTg8tWMb|TYU0;J*$M%s()~o^xjw-!Q?4vkDiK5&0{UM zLKGQ_O;)gM<6)8xUA-;gG=w@NiaK@KGH1A@^`fBd;9Ln?UkBMd)>>~DU_K_ZK}W<j z4!$Gw562;*tj=3%DO*NG8PHfoZX-$k+^+rltnH&O-olh9sZ*safzXsa<wZAPGL{%q z%VnzQBF24s*@V^XtO+leT_ujn;9@~+epxZO*-a?=R*Xe4OMhk2{H%-)W6D7<T&RDY zF9`#)#QMxv?EH_svqJGSVCzo?pzDERu8@V!(#~Lc)E`O*D3VBPbV9Ee{)!R+wEH=P z@=yid$CB{S57CNs7t|I|nrxspwc1$|3;w<O5dzWv+bLrJTMI4j%^>v_14isqeP@8$ z2nPs*!fYr3PupD_(m~N!rmX3=l;tz2qMemrHEq;Hq`s%_;8rI-fWR?8TUF_@eL2*J zeVHRPOANs{r36o^fSN^7MvnPohT{fi35lpki?y`1K#S0?{ugy)npONr%>)_!NE}`n zj_$pU?s4jDPj?1UvcSU01hti5PZWuYhk>jdktA()4w<(kZzpt5QPW}koOFE?7uS!j zV@<hIsT^Rq;m9%ppuetAi~P*<A0PZNcImAH#KwOh7(3h^wtmxO3b(&3_8H_3VOYnh zy8L0*m<N|>W-akts((gq_pPWN3<`MVSt+}$)z5W<xkg~*T4akT)uKI6dE`2#i>=sG zBWJm__zF^hygFJi@F#6tuyQBBf5V-B2~;AG=5sO(v&R5)T!aT{`qPxu3QVgDL#CsL z=BzO4G}D>5S$lVU%v@t71}=h%Tvmq4E_1NjxJf0i)^Rf2+rnzWL$kBF(<!4}FBJFl zIW=FJH{hV5BGWZ-Q2~KB*nXijwow@hcvGmm;q6GKC)V*A3^>NX5baUE*m6dmM)#U| zD}>?z(3E|1oFk%yNmCbbQBmyDlvdj%JsVvh!Z<7D6=Hl^_hDqGx;vzyG}8$_9Ol>| zk_$jD;H{u$;f**vE(K-guubF3eHR*Q*-bh;hUF>Jo<yg#mz&BW5^*v8gY&;>G)wX) z?{Pu7Vf~!p_-`H>%m_@B<C=zX<j4o~fP6D4$KE?)msD;sFYT%G8VlA|w#PXl*v{&k zG45*jWm8+XEXdo8bdTkCQtdt{uq@2v__lzHVqP6W$ky;fZR5s?lOo=wXrV0jlsVrx z-o6pMuZOHUUb%<nbYbSW(~$<Yb2+ObBwumr38^1Cin^F`Ic*EdyCnR_3xGa2#6~w8 zvXsl_LU;t#DQ9Xfr;uphWlEIkXWaT@ca$lkG|M4Mx>8&tuTIRslB^3C;`9xCA#S$W z0!@1!byl;^|HsUX%I#eyMg-zlj8WV{DB4r2ISixzA;ejGz5YGZqPY-XV&nBkt7+v; zk=KVne2MPt1OUEC<?`>$MI5RilEWlUA~gO!kcoiCStly3-oZ^w(RY=f&na|0W~ahV zSE05X9O|Xc64xY5jA9rGqqXi|$XVs=c(X6!rCgSF31iEfP2tj?BciwcViZ~d<^PCE zIMj~$&0$3w_(J3oD4%7fRGzFIeQ3;T>kKv>3DDHzuxg$o$|vy7EIDMwdJ<jRuPNuP z!Yw`NK5W=jak$S+3Xjfprnmis^ZQR{(??8M;<N5GMZAqbzUfG#EjMikS_^MHzFWo= z?PaLe_(PR#=ki$a4M`WIhSh!7d7@KElIa_r!4<u$exCjR$Vo}-MxUNgLz?*N3Bair zzGDjM_Z6Sut3u>B^mJj96bG!VPIhuBo6J`k<W;dLP7WAD75PF?yb?7>$!8UVJflq+ zv&7d^GXDRhw0@DIQ~E{9_J`AdcInDHEI@78=8=8)%HH|8i-O<`YVAnQ0diLG2-5U~ z>ACcA5%0b82t6P>m=8L@K-?t^qb|I#m`Q#l<c!hyt>}O9qRHBmYTk{XJ1p3Spk0yp z!qm^QGIrn<`*F~e3D!%~H(`l4MIR2=aayB9KFky45BdU4%7%}E4u{Q;zgEaI1W6Fo zuyaHjm>EPeF0Op}yx-aRZN(!Ojxt2_?aa?s6zTy*8Lm)mmRDqDlIiPHCMgV~VQNRY zq(&Z%T!-_BD-(A17(*{cs84;ug1y~7Z_aPGIy7cVAkC*<(X@x@*6XEO9|ZTE#sw95 zFAytxClXtRK@{x|5dN$hlFNk6pqHXcI_hbD4YxUJjv_-(w7>;hil|>1(*A-Ol#*~p z?>E5G4)rN2ih*ZnbZTQQIp=lXklLZD@mhdbNIs#9io4(r`<TGrL1n9}<1nq=uVA4E z?x_&S6zctRmIqRXS5=ZUqgRN0w=7=Qzu#||<@Jtv4i&QciG~M0Agc{FV3l8~u(>_C z{%Ri_t94XRW%`EC?w6PWWYD2Ei?dl*lmb+Ml#DPiqh)@*q8!M1I7YxGJR59gRCYV~ zC1Ul{HsXXOnzQ*wHZS9zfR2B<Mv<UBP95BT9h(WMliwb=iuC%I<m>i*x%7;1F^jv# z5yy-LeZ|_I?X`3A9+>{i<D}7I1=M>&=%d!wi%uD*>h*d%$-Xs7OGSfA$fZ&C2<Sb( zr(#oW>s8g68Mqx4EJ3f6yv;IxCI2vfUFhKPhiez`!)@~e-YJ}ng+7!vjrt=eK`f?^ zhxF5?{;6SJ=wro4JY=xUIVzMmW-o;|LQ!fsslE%8XiR&c`l^FGvpO+~MiazQ%PT7e zW%WHq?Hg2QT^nvnM*TW956~S;fXSIV6(+YQ0_KL^VH(|K+=#%!%o4oA0_u!jnJ<Dw zYZhNoSdo5nGPN_Oa*FxNAQ(%S2N~Ija|S*ra(-VN*D=}-XzGpj>GKywEHUP#87Qk? zb12>1-A!zZ^%s0u@5%TEQ>Yh_$(75N8QEAUp;<{`@_K3()pZ}%w1`dvI6f)=+Co`5 z+K7K5xtvu#P5oHH`X}@zrFhPL#pUBJ11>nMMqAy^QIAED=#)7b)L4bq94~Ld8b%LP zk>*T}Ga&zlSjDY%<<AS?5zZyKoSGzS1i~~d`D;x%&?<JyZyFn+L|PylC$8FULn$*h zXg#UxV6Xc5quJ*ZOXvjv{O-m+-k>!S+{JB~Br?rb@EcrCI<CBEe|cAwwwlo{dfI*q zFijtgv1$}BHPiXFN~FG4p79%^W)@hu0#@1-Ld(h?XnkV9k+0_aU@9LuK0<&tsVkn! zwCKe8ggBwx^GlqRsUJy|zM3K{b5e*k9eVzj@Mj}qBSgm5IxUqkfKdQa2$jkR6?iW~ z#njJlr4~_|1ypt_pf*6#C;{0K9%h{z?uL3fP({J-s(C}&(bByUVJ6*aX_#jAk86`q zS_tMdjIbaKTXfi@^?*0qN8CFqoW|aRf65edjElc48W6(^Ywn-Xrk~!`7iDyZnF;1+ z%<B((C_vzr#SUNr_%QVP^JazAll_XQHD_TT#=V6osUD%D^JZ13A2XXWqldy96$UNF zh!Y9OECusxVhic3aa5+~ll-F6S_DXzJiux!*bn6Kg?L}QUBu>LdjHD(oXH+at#UR_ zJW3wMYiP0oUribFv-H~3%R@a*Iar`g^jW*ZCzniNGl<g$?9$YtM-K=5ztE|G#~^T% zS=f6RQi76UVtu-!$jNyYF?KpYr$kD-D55H9m_R<|C|ar}Vua;mGwyl$?n?j@xVHWH zh)&~vqbwm;ij;rZ`8mY0Hg#F6zm343eYNE(D+7Fu3XrFe%I}iTxGgjvq0;^7JC)sM z6~H8@IUeMc`Ws4W^i{>#qcII4CW3pP1{x&t;XVs{0<uQkA5O_+)XV4f18zm=tc4o6 z&%yUwVc7l>WUzenZNejz6P(&-V^5YJP>AvoYOH)+mPk2Wzr+8{cV+kn)WdA78F(tl zc=$q!YpP*(pBgwU{CGMDyDItf=xb^VKsL6=D?5KdDs9N8WA7$iXsm%|NW>5=xyVyq z9t}wKEd#Ur&9~=05m<ZiGQ51M!6)Z-@$%e#b(RjZOaAUmZ;{|w2dSaC<M<yp;PRT) z{8;Ik;06dVP=fiZ3XrNc?Y6UHR@FxFv^Qvhw5h-_dNjzq_^WhwCtn}7v_Am!>GLDC zhWw_Z;(MIdg(8iWysypNKH?v{2oOK$tFK;E<a`>xgV%U3jD*oGI0}-G0vnajM;)B* zn(4mc5V$*ICBMqQdKr#+cn~C^gl3T=BMeJK+QXX3BK;t|2;`X!=Pr0ZAR`ciKRL*o z6}6Y;eQJo$G-f?}0kbTs!T~7AT2iM6Wu^#{NGP}_fu6nme1wE#KTZp!D=(l~znt3( zJ5qC$JaW6EuE|;zPL#qMQJ^q=Cz{*j^Ci>(MxkFm+MYw+#*y~df+Y15=C3TSFp+^@ zrWg%SBnmf(S{_&vNBK=8p;C7EQlO+muc+o6UX=1WR$gL+%J2&v0M3vKlw!bW{x2v0 zGXImS$*?7iD;g4<w@^pstzJZVX36WLuW!}sLj(pYT7;U;mjq!1NI=Z76*amd9L@Q` z#=;h}G;NxpoGnZyjQ^4IaArb<LvCE!^qLp%s9cNrr9V0@g+%|o7t)WN5jKqEYV#8l z%lrg?K`Frg%AI;EE6~qx7FHHKMr}OIgmQ=n`u~(Stf;&laPRAj@-;RT6WQzw(bUis z>wsVISN`^1z8313iCOgQyylk9Xj3J}FT-W0dYGzJ|MA^ygvvomZJ2Cj{jQzDIj5l+ zfcl?nR8mWd8%_@jrv5g0%+fsefHMGqAh(&Z@~W?7B!bU8)1LU*+CoH<&C=u=n)N>t z$@A|QOo>8SROO)>%k|%pRn`fT<ahfEHb@XaHTcmi`UZO&L(bCt{KDoa@SFcHZ$3h5 z7A7v_j~!ou&@>vwR@9fGlnghG{*L<3<|AxHjWwN3#O!ap2#@NI0N<2th?@Tky2l9h zdn1~VN)I)x+kffMsm?3Kt5_<Va}*#c0l26<Q7XA$?ct>+oz0Ae64Z=Ez(IrVZn8MO zoJ&d<po*}@5@U{6zVnsj|AZIg3Au20vv#>cfuZjVJThaV^*pf0h#iUfiUCjI%d;7o z6RIqL<f5ZsZ}T>4nJUNGvGUgc%bPlERe+J*m+XM78Wx)8Ukg{2L)suVpx}S?&d$2; z3rf@V|4UhS8HzDD9r*w3dcm9KJ|&u24EKBKztVX2lAr;~oBd^ieHIAqVfi0&euCrd z#oQigH81(+*i=9qAP8J^7S@e`)F!^9qq#N|l5T9I_{*$3Nr#+nq)~AtYnU@SgyJnn zGV83(`NhV<*t6wlA_NZ=j<ZKRuB6i|&S+8CU^y2}G&yF9y=UMxd^~YsHtSsmGBz$O z^?#Ex`YSRHo_Vk~Zh%BmzOyuGf1Z33u=?RbeyJ$>%)rV4zD?0xGaO2_Ui)(4PJT-V zCbKTxUXeE0hX?z!@<hR7*t2hnH=c-DjhmVN^gpc{ka`pPaDQ|4|M&W5@nvYG9t72K zU{f|RbnuBaTiyO|v?tnV=Ie@KeE!Ud&@?Ld=_TwXu(t%Qbko&pWw|N6-ajU>fBKLd z4gQJNV)*O#R~ly`4a#8Uo@=3>Qduk+bG6vjxaQPqS-;}o<Hglc*ITncC(PqkMh@+l z*(Ha$a4BMFmTrr4z~w2$h;HYtNX-<%`!b&tq6VVwZGS{OWN(nD#+Y#!U@1Y^G2vh| z`csjjP{NSRqX#Bhh879}hag$xI`O1L`g8~jKVuC6#YzfOb+OTqy~K$-t<19sM|6NW z<qZcWl^wHkoE}@+u;tN`*nyB|L|sDM4hwsZmm>W$uTN_C#b&Occt~|Tv>bz9_WCkI zuOz{ByZc(Go9l3y(UQawg}`|jJ6FU|AS*f%jpMn}PkV{nCMg;jH`cM~5x4<(;5)yG zojnv_YUDY#(emS*J=qkJ;H1r#A|b1FohAy|DW+`U<@yV10T1MmiilnXHU<-KN_?Qm zhM`x$FH2FDuF$q|QL}b&#WloEOBzGUlLvHj5xcn;(^3aBOCK0^0$C+B+qpPNi4?e= zi>lK;Moa7T2)D9<rz%nEi7TU6Szd2v%mCqcPj8Q>=M$1A)T5`Xyes1TduWeU#|FMI z2SnJTjPlM~BI$_#2tz&L4SGh(7#2amb0o}-w3nM*QZmJIVny|#wAl5wod{A8*BDz- zQzA{BaAZlwlhui7(#RXGD9asIky9>Pl;R+jj-8e1TQ5^1_Bq*El~pjk?JdX!3~Ns* z>@tiRi&>2Y1n-?OC9j%Cttn&{C`1;xDw-}i@<+<bY47e*K7Kj*A5kD{dp?DpX}zp{ z%$jUWNk5n(>;&FzB}09;C9D<X!^T&ygBa<=E9=i3k6&-5G;)?CQOq1A!Il-iwc)RA z9TDM-VHpI4UCK0u8{FBx+*trKcN=pX=pFFC8}0vVTs8F^21Ss%<x(J19=!A65>)zp z;MSQ?vEdLZy+6Fk=Rc7EYcZDRjXm3cN@K$x*JqA=o>+a2%H2rR`*c1&E=OC9p7h)- zIy8TM1R0a}f9+A9I-l8=3LM28(JC8&rBzzC==&mI$<H@1U;!qHwf|~!)<{I9LZRlF zUhjN6kZl#9QomA*p$R>t8@Wvp>yZz%EagnL;lwnwoj4R_`x9cWIAzdE>D4n#k+zT( zp*u_E<Ose^ek6H~@sL(4*iFt!cp?iq940Ej{39q(!d^beayM37F$6WkxS#UQ6aR+o zTZ(zhu=IGLy%<2+;Lu^Qu%&Ju(dt(U`9KTA10iFegkENV3W`*UBcwi#|CoZpPdrYb z$e*H0$-1y6uijdL)>PaX%xi%y=k4GpN*~lbXT#y;NMAikyXx__9P+$d0qh%yc-(%( z99&CcPyjd~%Z#A*dmlpfAA1jL$F3Wv&h0LjjSa-VhRy&`Z28d5jUaN{i{p9MlSx+C z<esZt*DTj=iC-H@*vYR3EAt!n7^5dM+r>lN|NeCV38o?s355j)0|xg+7a7=U9a`EM zDi~N?8yFbY*C(EK%r4d@&KBnCuFgLltX!Bp?d{GDbX+!gF#HX*UW6O&o7gq?WSaJf zbfwjM0ekDK3)(YfkCBq93B$;!jPShkUk6O~31HB>2{LP@DXe8>Y?wskYg^V}B)twf z@1KI#wbEELhFJdAEk-~B9E2_F{m^98*jZF9*eik`LAg%*&#DnZ95}Q;-?z6w{IVE! zq9B%+xeQZ*Gmh*?e#yY(?kG#zkgQ)=uqa`DfYD@2w&+B5xiYxF{f@+~x{g_ad!fja zv4>=zb3Asyzr1&IZ&yUQR+7?JAns0@$Q8i>1CA_GLT1b3lE^L^MYRmg9AW(k$z~q! zdnsC+$%#p?x;>JZZYbW!dv#pH7=2&a*RVPbAIvya1$r0<EP3F^%Qa4{0~5wgA;rEA zATa!(&CAtv7XNh|C0FE?mm2{B%k*R=Yj^ga@>^mv9Rfw_u%%Js>Is?q;IM}i7V4#A zO_z1ZWyGoPl`?FwX2f*3;l9t`l^Kf&!EQ8L@#8v;uM5_zf9t&Hai%eKjGI>xb39FK z0m7mVP(M1WBCn7VnX74knK_odE^O<}0!~ADZi^%DbA7iy^9E50D_oT9FQuB)sE7Sy zq>NLwn*y5^e(!Gh8<G)R(0aMPcie+L!r)1j+1X@?VvKALCo}9WXHL(J4=(a#DVHt# zp@wlHrQ(PEyJ*p^9pbL#PFf8InQ`FACrjdmMvD+HX5Lc7vb8LKHi9q#(I>-lfQuFJ zNbFcf@(XLR&0sgoK_|(bO@H2e!@R9``o}(z#wR!<{<z})g&wqK0_uo9nlxB5@BP5C z+DynisAl>XJ@$VVU5Ub|V!&YIUb`SM*+yp_B%ze&QzbSR{p-IwF#|Egs>N^2rufTn z12nLPa7LW^<0w0+576C_*ityp02dD%uSiZ962aH$pg@~YI3d{KR6lHA>jWK0&Ooi@ zX`VB_W__BLFxoS_zvIqOswI8l<@=$dGn->SY})H<6eO;(_d!DMiSM#i#+^2tSzDNB z8d!Qa=Ff${p#KF7l#bkBThrKjAj~Y&D2^%ZyhPVrCtz_bf)-_tzzq+A008XUlZ8po zYxdbsv8_!}73s?kEf%FCyGwAg{Y6sMt2!GDm}Qy_2Ip`Q2C3!ic(n!~B7r4JWWKWG zL)QG8OWZc1f9=mvhW0BO{)1e+=9QQ3$?w=lh8q;oaAPBpAJaqXzoLw~488sCbsHJ_ zW%RxAXF;n@HX!)#r(^SKfbs+VPvQApJ*;?!&PhdFI}{!6ku=;g_U{a%MPBFR8;E1` z-`~Ievn(Zq)IIfqpOITz|AS*=c<7A?W#c+<f#kpc+qxr|HMr=F0-Nl-I+n}7f(s!d zW_|?qk0kmu0~3TxNe4?ggXmee5Xv|B++CF+erS%Iq9`S2K>4YR2Y|5CzeN{@Vj}(# zf;j=qPSB}DVt|2%y1~)~WOcndJ`iwY9^l)l5lC}!HSkvyVb3LCMkTlqOal$f_z!hg z-`6PxlfZZ<sWR-2kYM<D;5jGDLYcD0)!frl+Gs!8ixSMa&XqGARszujD!v!w*EP3B zJu1h}Qu-sF*zSCWD*%NV2u$8_I)CmD*d6s~=LDIE!2(xnkLJ&0WKwp!&<fVfZGNa= zLWUp{%5NufXY|3zA4@#=)_f3`QV)<A!D+d6N3LunCL_7Zorw`NO2<?UnJRXw=Xqs6 z`WBC~KZb9+^YlQsLbtWuOS*Y*F0Rl`-(RUV{9S9fF@S-M-~d;_ZLQ0jR_A!LN@=B@ z1^#zV=1-+p#a#TlR9y@77ClW#(n*HWlK!6<=O)UF!c>-cD`I>+&e!+Z6gTOwI=^Nd zoBU4ziQ6f}D%Y`>rBaYkC4vQSPPH(Cm4C*(z|P}^HlSQ$g&#{t+xqvT7^1YC6dip$ zaBr0{>s{hSgaE1m67|JT-z^<h7dBb;kd?w?Dr23u2CS>y7|<v1RAHW_S6u`>`Q&!a z<%wWoRx8=i#Pildz5xlPE4<AJrxFnFh33(mMI11Ytq60b5MiuI7if~s+o@u~eiGG* z-95|s(=8qH4bZ1vkiVEBOjcJ#C^p;<Vv?_Dro--aZvu7<ILTR{L!;*Sj3x-B<?V@l z8;yy4u^ov*X`Dt?8H@;|H}IrmM^#lStcmziaVFFEdbE2re#!$+l4!7-A*QW2Ii1Vv zedL9N?M(Dj<}?F5AI)(5g)au{L&E43HhltiV74;bnYv*gQ(DWE{6yZkTT$W3Njh9W zksr`@;(#CXBEsIubP`JXPeO&#yiAq#G3&hXrWj>mQXg1%Xe;%oN&=$KX(2z~$;K=# zfT-y1zLFQ2d;Ym1P|7?D4@RwXA25N|?H{keZXF}YKN>Kl>lanea&Ic9tbzFYSO=5{ zA=i|96Mz63Vd-9FsYNfWDN-=blKJS-<bM|J_W-FOJtUJ2_=LkW#Yn9?mXZ2uyD%iB zE~=czPAxI!c6rfENQ3vsb>p<+<wBk|9MkyuabmeKOx`=~H~psil^+nd#kIx!zV?DX zypN#D<W=7Gsl$A#>}DuS#rUH%KU#^pe_R2DEvZBSmAb%ohs?a)br_EcZ|RL>{6MZb zZ$K@!$PDZN!g5WqdgIq2;wD8_oG9#F^nundcR4Ejbuhnl5#w=x*2jG;sB(DH-)fIg zs}b#Y+L?6VMYH@NA$%dOm<On3Q-R#LVMWs6*!Mb{id1<c`c>|}BPGac&H>1>aM%0~ z30Z$d`K4@d(0r&Ue++V}xN>+T*y@X4h62uIev>9DA+BRFPjGg2`J%!4&NJ++>a0bj zdW=&n4L5hg`+=t-N7*|_?Vd$2!X#+I#&MBgy*O&CXA3n*ETY(ZU-Bd2=qV}eUR4qz zJCtZ$%1cJltIm`zor4mDcB$`KD6P=|DH;Y|h5Em6W8VU~lj0r=e9>k~b{TpbPXVf+ zMANM%{nQFZyV$Kx6et^NcPw#-c(k!?C#9LHUlN~u5a{C_?w@3Z80LS9b3?Ir5N_~e zZtpsgGzL8qCU|V~RHCkighk5^YrMqhAW;5=I78)##j7P?5Yl(JT4>AO(5clIv+*T* zrzQn0Rp-#|J7iCY2R5go%lNQIwgBYu+urS)BFot$=o{m9FW|Sm5M8CMLb3Plha2Ca z!NC}6Y2+zkdFon|s6h~S;d}LxC5y_;*B)}p6V+?nc0<J&wiA9hCSIxwStwKGe-6^u zsehN7r=ZsNVGsL?hk3E#OHuud%Fh@F#+r(*NLga+58-j<%iG(UXmnR74g=QR%_?|) zB%!<67jY#9>ft;j75uE}<WkjYvVk<N2Db~;t%6%;J3Bg}NJd=X!s-4A*O0|-U*l0d zla6RmVJ11?)aX6a=#1q+!*+j7Bju$agB|w!_sivoJeP2daZ~L+Z;V~4*T&NmTg9U+ zZ)uF(_led8_o4CDB)gMHDPBOx;<$UN-N{7DO>6c(@{-9&6{lF~yWgng_tl!47G>pR zWi7ZMZcicMw_6!UjTHIE>8NRlxu5DqDES&W#s4h-pSG?7tg5Zsl1hlu2uOE>G}7HI zaR`+z=|<Qn-F<`u(kTrGX^<}IZlyz%Zt!jN{{OxAy$9d7WbZjv%{k^+`#Y|+PuKmA z<NLUFf(PfV2H7#L83@3R>QcwOCNrM*+odIB%d&EfT5HvG(%+**kLuXsk*0wSr$*|Y z#|f#!Ur)=a0LKw0?WpH%XLKGCVc#D@<79Q0M%M~n-q@*#gmH?hyYNgYZrwN)Q%^q4 zD=9*|WX<RoTsWimTM0N4!C1e3QJ}D*@-@DQJ1nsD<rF8^S|?01?yY9IGc9d>>KFR{ zM;io{?*@dh%KfB#$v!Q=Zin4xSAG7;wD_c~%D}p2sRbzKvp_2V3_}r_2RKZc@yiN& zmyDi$FY__jykF^2cwc28%7cG|XGml+CH0hUAk9Nec4KG$JUw-+UF|-LS#;wU&Sx-v zu8f~aC&PZ!R&TVRT&yperX|X_hpe5%omn4eb$IG55;TbX?99v5)DzlFpBRm*_t1P! zx(RQaq!j`}HF(3Oon%4}RAiRT%#53KFY1l(VqR2Sh!YQ_a-^{j@qAnA{w19AOJjT8 z6Hu!eV#DWPc#K*X*=W*xVtsV^O%tR3*Nj+=g4gC#Wp&PD^L;dkt7r)ME4RKR)h+Qu zOLUz};Xqq1wQ3=DmdVZPfvcqtX<4vl<UJ}$#{GLhc=pgsQCI6|>MN}^8(hSatM5N) zdwh^^GCw^nCyG<+yPxL_=TN$S$?SMp`H8t9*}7|r-}{sbZJ2gCl$XkY)~mGvX_x!v zyR55-lDcFmOt6oi^YL~YB9}l}vj2DIRi678%M};})*Zb9In9Q0?<9?jYwmYp?2T#~ z54y!J0#T=~b3VmW<oGObIVG>X$u=nJ-sA?G$DBs|dRV)Lq*HyC&UWlVF2lU0cU3#H zU2#^q#n%y11*+9%o39gZW5<dLmUv%yzhKih{{ep%aJ|Z0%IM7Y+Lr4!yCyrCTbyQP zrGfr+Qw6#)mCwFL_IKXfK3b1o)(MT4E@BG$IY8qPj&ozj6cpKQNbIJe^*z-05JRwc zSgcQ>w{NT|bUk@tsb=$(zF$)c*JAA!PNQ3@y+FXph8hc7ftp+hJ+6L0FBG~_$>M}h z^_oQ>y!^SB^PY~uN*`BviQ4E3RJo{k68E(qtY{7FQz&h$JgZ@JhT$<Nt*<3Kj&(L^ zaRXRa-FF>pto+SbmIrz`UdvQs^Oxcg*YaRDa=o(M>a9BJSv*z<KSkJlc1s<%m3U0s z{)MV=)Hj)W7b)sPeavo#6vidjd&z5a_MpcovEX_Bf`XRR=`zo<!Y46xN)fYdS<P-6 z9KJ6_=6NnF7LhoTd%oa(blQ^Pp?`ne&EgD{_Av)wi8oa|C#E<`ky_2UKhx&IL|kF= z(cIb0+Id~N?0b%Dg2pZLF=k#^B((S{wOQ`wNLE5aW_ZOW@w{i@An6Qg?TO&)h^M>H zWIE9?@w5!JeHIgX(TFTRok9Yia!_tA28FnO)%EH4^||>zMzH^qqi%3x`|#8%<u{2& zVEqXue`x#jtS=7ub(KNR(Je)FlG<ta=!!kvrDv2KS5SH=zGLF0t$ZQ(hzL-0i}5Gc zF|WpTc^J>eT*uAor%e#f-&8#-P$OyFn0ExpH(T?>Z92y6Qrc$G!<julU-?$F7_rB~ zR5leE#Sj^FwnJQ?-Zb$rYLC_9*uJ}a4yYX6c<79Y=a-e1x}Q|LjbHF+vPC}aWjmvx z{OI;buW=20pgT*5KJd7d@S#bGX{lS2R?MK}!5Cj+;02Gr`hvFw+}@2G!z5_;6TG3= zY5FWd>f1#c9*lFJ-L{(gYpRMcEYrJNPuFCRbcljOP@cZkAr=E#3KxKV(9=R52OgIp z7g^$=Kboslnntm9xss`roeHQkeImTDykHceO2e`@f<sGZ!lZ*)>tip_UbS2zmp5DO zj!BYPWf4$}n_;znY=c7jYAuK*fp?ud+&f+S5iUPQusARAY=7#HeoA}ckp*Skg&VQo zpY8nb4-fpX6t(0;=@Agt4-pU^BP57GNr1FOTZcJL{NPr#IUw}<1Fh<qIJR|HU13sw z5ya!d?#ca<ZYPUWqz|Xd9^b(|ZR03O{s|E`X=fsv$$f*y+Dmv^umK^tnMZLwHq4mD z<u1o31J0b1>3q5D@^rS)9OUt>jmw+HKGlzM&9A<AiWj;?^L6vAQjWNGpp3TI>7ipe zC>Eh`ITlYPz${1sgEpISN&Mna#2fDp(#3HUk^2mx-V1!l%reNK1m$&t0*r?!WXq+t zMZF)9mVMH&clHS0P@i}ElAK*Xr*A8Yhd{gUJCE4-y%xi)@*klA_y1IL3ZUcrtbBUQ zQMmOwet$BKp!xF(fBP8C7pPKHZBy^)@HfV^L8+!Efi!7Y2|SiWfK*5+i(2JVIW6Iq zsBC=HX*_<3Wr6wa1lynbiU;ChuL-+3yL+zG+);{u6p_s*CC0d4k@cceX-U~^kdc#E ziIzyF#|_Hqz=(@qPV)pQV%wbKHHNqeLKIAMD=-)OIQI*+4^t*pNs1qrppFt=ZmtlH zX%b2G<^TegrzD1`fSf_WbaPkUGr6;V1DrvNs>>nFYQ&nC@RxM6Ivdpsi%(mY^d5a$ za(~&=^=gPGC;>_JrZ+#U3W@&O``tX=qX5-Y_8*Y75sEI2$E#cYm(SoQ(&URa6^jLX z@@&W$Ymc#?-HY~3qkrWsij4p4US~8;BarTL$gpS}>U%q&;tv=wAeeAa9;{-r#JOXK zHsii=5)2_jF}WXB6_F%<kVY0tMjLT+{4-s==XQL$BqNH4{KEv_bx6*LN=0hv@^9k( zE^<HYMIZAy2RU?ka!``H4R5yd#l~6WJ9Ick3W&M$&rHwKdSuvCGU`lf;ZJ@L3utSj zG7!!!1UjhmF1N2Jz|EVRhr1cAw}<0mx3|k;*Q=Wj<Fynmzc$N(EB0SE>nHP%0Pr;r zwd1R`6yW<Bz^#lo<?ok&tuB{`#@<?bU!T;rUeEtxZv}qsowcH$?sF74v|b<2*F*!& zI)_L6Ozh_#62C46E)EJPz<<=lZu1j8H1jP1wUh*iv4}a<`Wi8XnQ*Yu1?_s5?xBoq zT`VTCg8`XXI6Sq5Vdsi*rYvpxsTkmsXg_nDHY73JE||f_z+<W^5Tm6Icgz*sD|ly2 zflk+ZpwA>`;{ni_r(bh`2bXW{Td!w6x{)Z-aLJli?Ads-Rom!sY&TP8>&aM9DoFro zB&9E=D{te}vq_SwirStQgk;KQ>G7ru(I^JXXq)2N=|<TSm&;0&kcdOyegF73j+2=q zXr)`W5piM0vq9<~AU$5y163;Xa2xIZd|A!c$>Z!nhbBK0Wu0t<m$SNSB6V{pb+Ev$ z-6ttgZGgW(7wkdK(pcZVs;0;${H%$64`LpIn6xLaTCOP91znFV^X!}}z6dHGzL%_Y zR@O>2T-b^g#Pb%8%8$0CDwmTeA-@~nPMrEXn)8xGm6*Md$Z@nF_MlZ1uPx;~hch&1 zwl!3yPy&=*%~&G|d9R12zp6F)B!GRX_wUX(JkpEDJIEeQ)x^SI*mtx%(-@nDB)eFB zNsC)*-#2Eq9?mpN6ZmPLrqWQNeau)>>AtfUnYV3&o|8acpI9@(ykVh*kGsv6A!$_< zn?xs+6p5@K9``zbi-ifbT9ae``clgwlJ1_Tm2O$W@d>%l5|5{DZ(sK9Cnlgqd#a2T z^2M1_X&pY=uIb2jQnhEbJp*MrfSc-1OhEJ}I_5@^tn`7sO$YZ%grD(_`*lkKDug#& z<7--Q4oLqHY7E&g=cKO|E{%j3IWmJKNvI>)sAuM*CXG3H&M;P1w{X#X<5NTfemeFu z1^TZyGWBzY&ma?%5Pbx;U7$Q@6n9X$-K$&3pm@oCr5w^PLYSQeJ|H#0Pu_C#Xsvkk zG!%ZTX((*1X+VHXy*O%6K0v>Ti_adOmj~ZwvB|DjC6q?~Y{*`*<cA(wO@j`a+b10~ z@qtTQ4K!h2cp9d9pxe@>jusx0n1@C@-pYax1_QYVYoNCwOQ4Vn3xJYouGu@}SEtqM z^)PL?G@ZWV)byFEZbbdFJbrQ9;%8GbrDc;cSOt@)BTt#&#h8M_qrV(Zc#QT<iRknR z@;8S9ofMz^R1dC+1zH%*7SXcuBu@!H2gb!qx^B79yq4VGWIqvF9IjEA4WYxU6(Y<| zma!dB-Juxtq=akt8UgxgoKMG;(ggW6@sM;KHR3Zkg>H`%&&MmVj|*%?o-#nFw$xY# z&~d(O%E@|%v9v`^1j@hJQ}U#L0Z)FPFY@;0v4Zf99*Oq^o(MD8_tn^ly()(j-=K{* zR?Z#^O>?LEyZ9zZ)Y_zC$*wj0w$Z-EkVt4H(OvS!hW@p{1R(rts^YkW_XFED8{ASO z-k^bI?uFl=6ZVhtG`{nAO|)~OXe<@s(_mv0&mNPH-+XO~yAvQIS_aB^5PWBq?(f<* zbMJqLIj9SM+mN(g#Gtg|5dwlPjaVWYR9G-GWiWpXXol|%XbP$jLfcIsz+x11$mBAy zZn=_m2k<Lp-4Q8e^@pBJ``uckMs~Tjx0!ga^62-+)WcRyAWDMzLJ`}$ST08<-fZw8 zc>`%hsuaklQ2Z>JEMNWb;zbI!q;a!XOsx4Q(~tU`V+cfx2tMV=lotM`;RjZyuvo{6 zWRS7Yy`C?)RP6aR$&>5#)viLzt5MUEen1_gSqLWB*6v)H$i<huZHwI#*xL15(-f=E z{g-xbuUGboIFjgctgpJp>raA~om+Y+GA1cfC9-4jq_AvB`6H?fxz(H!&1g$#NsAP+ zbj{~|qg4x_)w!EX?$;vF3mfI|@!1W6pftXnSEYQYBvuKr6|jlr1Q?9Z`*}Gj;9A65 zAie4JM#K-tawbd47&FK~_H}8BVxoDcK0e9fQ}3D2%TJxiCU<Ngg)X%aE2CTJHY-zk zsz_}_CGl52R=&nY5&Nl+=1%4zGOtj%Rx}kcgD+^x@<@uq;-1G$YO`&fY}X|oiAXHe zs6S>shW9&PZq*}+wMu402B9YD06(91hBKSLQh2U!TooN4$l&Ku_dpLbijR?d%v3lQ z3hEEMiLuX~TG>e@$;r`59;+@TjoBnU)=A|#$Efglx)9Z8mz8*s8cKXrpKHj89;TwR zVF>CwCMph|EG_lxs5SEHhZhtbSCz7UaJ#x7f8RuSNF^c3{X2Y*xq$AKSH%T=?V5U8 zlbBTos?D>^PJJD`lPQR%zcGs+>H?+oZPe@g;sdi+VIPf0-hQa->7<QOTP7f~UCp-@ zxuvIuGs0Pv+TXI0S|-pH3)-tsVG2<Xh`3+GeKWgjgo;7QbpZW7*F@B~Sy2y_J5>AR zT_h{L@}3UPG?383Z;)LRn_+-97)Tg&YW^xVx$5h_RO#$J$;!Y&U03P%K5vd_Xa)i; zBqJp4hq@HG!JK-X95rz-Qs|8Ev~_~Tap234-^5Ae*&edANVOMz`AOh5N{NiVYN+EI z(X6Jx<o3;OYvw}pS|f_<!93&a^<z~5230Rlt6LJOR)C%J!?&n{4Q@@>6BpTcds^{l zoug-Yv}rL*!k**Y)$5~|ll`R$d{c{R(r1sicX3*?w?6UF@POHOI46DdK0Z^W9;MN2 zxWDb!^Z4<6Q-pGwjZ@?k8%_h(Z@#6LWd8FFNh8@K;;EWGdX5(cC)JY^-71>Vmb*!2 zHD2HAJ_9{IUk)I>=m)A+^l-*CDUrH@@L@8m>Npsmg5Z<q#kybN+pMY-Kb?AQvofP# z8+?ftRfIwJ<`O^No3qQ!SJ8Y(b?S{O2lr}VfP#^o!-j$3-wQ$G#w}5!c|B*g?ugbJ z|3*8f8XIWnVJ#1OC$N(-_?gO9!-`V1s6UPo7?QPF*`ky8sMY;tVpnh6#^v<R9D0Am zEdbsWbAE9KT%SxIimsg0bP`?6F4DxRku336^JJxfb2^b$nP>6DESMH`)_G9Fr<0&k zZfo<((%dM<m6xtyXyOR}`skM$?a8DLt@zPpg8elcMwB$O8>-HOwoeT(d2bxHEgua) z2fOIZVn$@?@={27U-(ayZMi*n1Cbn>onQ}y^$&2HYoJwAXRbTRy9GYcIwe7}T$7Q< z_%AMd<sH5ew=(vzY44YP2yQ!j7h)iyGMKB)Gj)RddcUH2(`oU0{Y73(P3#Ns-6?l| zx%Y@$)pe2-<hwezuv;@FixOaYcDzll)J%v`MR_TlP{Ro47<j9Gq@$KVhtAuh`+OIb zEVM{;aW#KhQUYpXt|IJ25BiSZGuiRH+Xjo3Uq4=*dr5!NRv26OO+$}7=wQ}R(P1;N zuYvYVE^~=O!D7K~ZLpQt^$YSx&4h<le<F165#B@PmF`(UR!JsJE^<?Qhd{s8Z6{64 zQ}z0&SeVYE^ecoBE#Uk}c?$IO*OJP*1M9>pNALPdn9w@48BLkwdvBc6`znlMt@|IO zlY1bAH#~@)mg0AL9Y>hQKD;76tEYk)v=m9`ZKN8Z%6W2;UU&#$jKoBNS%M5Yb$A^0 za6Z`*aBTsdc8{&5vGAj=>sGKr+P>r7@fSw!KOyEV`%t4b6mDD4ZL^cO>&zc%{Lw(@ z<eq{F&;zlun_c4c->?*qkfdSbS=c4$xa%?kNs#TBI*ssyc&E#XF^JNhu|c#lE_Sb- z%#Iyju|tYk?|d;7UQWoTGoEc22#iaX57->cRF({fnUmSJjpNvglzsa%3zDLm)s-h| zMQ=d55awl$zhj^t+BnVx3TZ{%uGQZ%`2zWBbZ|x<@500eB$gJ=DNv)WB(D7P_(jA# zqR&MbHe5?khb2l*0-X@2vP(d>ynGpi1syIaV=6IjVeb2w^L`z;R_zHl`Wc&g#xX5< z8>jR;3+HbL`K-m9b>1AoD?i0)Qsu}rqZM}<Ff1KAtLb^L;lnf8=s*_a1~n*I8ZYn9 zrbJD`)qC|Sy>UQK8?LRB8#EANKt`bPHH>RDoEgK+YS6?kwfzxjKB^NT-1vRtYj3G_ z@`6hwgG=B~-mlP5@ua9@!xjWy$I_ni1=H`I2xQEq*uKEc5L!5Uf!S9Wr(gUF!$>x~ zwk<9G9i~Ady!B^Bya|3;94vFTX!UV=$@+KIw&RgToS$K*E%=X^QXwb(OX$BfmA_UC zw^O6qc4)_P9Xq##mSxC*0rxh3X8D_b3s5sMyqw0@WkF(fN9@c&_ocQ67FHcnIQr`| z=jF7;Dh{i^*3;2`Kp3sG><Y&3-920K*K*@#J4a2}fBdd4{XlohSv7U&$NYfED{ze) z$)8a#zycpS9>ST{Px)^RR8&%PpYYw<)=SONx_h0=eo=Bt`;m^sbC+wawZMn?e9ywt zN|-=j5t^T$nu1PRLJIWy{yA==u%THCbi6}{?9XE5_H@i3&56VsPcCbh_}lFez-HMc zm`WtqBye`VIlZFwLBRjFG_KJ0P^%`pK%T3Ro$Ol#v{o&MrwGO5Py1XSL3-7=ek+Vb z#Md{XnmTVVw}9$O*khZOLD`n7h2R?f-V3L=<n;p;t&$>ABL59tklXGQzwr4h#WXb| zHD2e^OJ4}S@PK<jz9_o*?Xk9&zYR@&zidlIO#j-h<+sLQ<$iMt8drLtV#mvC9dA;8 zK-vfS5(S$c!rkTNYU!xygvc*(!x8!4cs--*`rHqsRjoX0Rx+QhQCfiX3nXVo81ZE| zeNlW>$fYBkvT@nCpPulr+`Hs+XE+#4D2Be#3gSv6ve8a`FyF|-G}jTBx%?*r^Ph}U zyCSk)Y_zLicCzKLNI>suTj{<>mLvP*4P(C_Eo8-1tmJUB?;Rs&jESlrF2SofXvRiR zB>~)9kcA{9J?!}N3@O!dxvL5t8vAyVp7YQ)i!y>-v?2#}L=*Ezzw_6^4p0_@@+#~` zH08{^8W|$N1?RV%5mZ%mPWRvr;lJ3uS&&a<SKumnoM`GM_(A9=ebeG<7^t1eD{_0U zrKdEhSd=4O{Bm>8(aWMH5=U8p83T4Jrh-VXw+kXMOSmDzhE`=k;G0#_gv=4xYBE0v zOQP}SD38b#+jDpobgiJ<&|OfC&-T7)WcbuXh|zB4IIYYDPQ>|4(@@YXciQSbzb5I^ z2OJs3IU%Das<3n;Kc(-7jvNkb__<6LlTpOt5rj{^E9SDkd7E{oq(ndm6KP~xd(~l@ zirMnAW|%qovgVHP;PwGOD5c=zJWatR#3d1>IIJ`uTx=f{qqA+uscug68m#z*6o=Ks zepmipMz0w!*l5)9<9+o?0b`;$k<~fAOm`Tc)hdrfgsnU8sw(S~MxoRpGKynp-Nv_C z#wp3-^pl1^$q6h>_Do85zh&>DsU<#OVG3eHU-vZfUj0r!f2||@F|s-zGc%<GeAG5l zREb75VlIdxg0{N{-Zo6;)xta*d-%$O+8sx8uvCgb6cy2CXmMCzK;74F>#D?M58j>+ zXl(0d+Y2(|9U?Ds81tGq(doAZkE;n(*!sFPDvVw~1#q>{?Ra1B<R<kXPb9f2XZU<S zI?ik}a1o<R-a<~_(&D6`iD25n#U&ytWv0Ql?K#Ybx@bH-daL*g@g~H_?idaYr;*QT z+E`Nb%vS$)@seAIm<)0}9&Dkl=!f{Cct)|ov&pNPdCN7XIGy~sH<(o6vl%TS-iODm z(LX8jp8zF-Bm?q^+T=%E#x@dS@j?eWf^Qw=SQ`?<X%s|?L9}$%y9*z-G7(KeLW(GJ z9>tG6hdG4DT-!qz&}}NGnGRHWCIa5Ek<xLrWGn58-H?oVKC)=pb)FK*2ZOzdH%Ova zIuVz;{5~vk+f)bh9eE%;h`DScnWlcxKTI8Pty?%#(ASIMfod=nk+Z}@Mjqw==ny*? z>EP`<&U9K@lu-yFI5m~-EqQ9^YzJ>PicZ9jvY;V0PjK`wgiSb~vL`ExR&h=<(7&k@ zNFm2A@>Am+eIQKyM!}BcLi;H$swB#@;xzPTxHE_Prt<8uV+<Kb-Wy7~HfhRxRs7%? z)nytwjZBhyJ{m@Ji=J9x+jX7HB^#MTj->;c6iwzWEtiQDO2hU9JI5~QJWaatz8j;8 zaXbS@v2Yn=hd{vBn_Cyk?2vX{LZ4qfYZW?($2A%<x+zT&UuQRKW3$Ro{A2)1Yi`Vs zJ_nOy&&7Qrc4PD7S;3>6c|5OdwJ0SZRlr~Yw@<#W%DlKAt#hv4P9c8tgYuWpYNPs> zO~@Ejxi{=ri@WzJ>{$8Rq_b!r4?b7wZ_lPv$a#;y7Eh;;>#+kVWKCK^48!gr4D;_( zxu;C4X=j;0yYARkHEGtZt|_R?s-W7OFk6llu$At4rtLCQKd-zxXb-zbk3r`ISU+J` z)HkfOb0dzbs^D)@kALy=cy1Py#RopC1WJPrn>@@9XO^u#OXs}9`lRAaqRU#!N27I$ zX;?{nan@kKFwUFibfi$CsYrfdaYWm;X0H`9@0pF~>rnNwPtUNzj+I7JQ-p{WGV(L) zm>R{lp<!Q-lta(PUYN9?Ev1ABF#WLNVJ<CS)5E?M;P6am&J6{QC(ebER{c`ogdvZe z#=ic+3o(a@_NwBx>e*rR1pSr1o(L%HWqI=k-FWldx8N}Q&t?caeU=QkI&4f4)UVx$ z<Kiz9lskjgrFZhK(yNXE(^Y+~J^i@Q!94f+R8qA=93s;nJ|xt6tf3QnfJ$zOS^WDt z!=Q+ivmax;UdDAwkF0W+X01__d64KywwZQsRDvR9e3tZNb4vVJU&7b2e1mu+mi9=B zu-r)y@uK|0WJ$)!tr9D6qxhhsjKvfYkDJ3yN$7BncA?9DLn|=ouyyX#ysG?Km~0UL zIk{0t@I($IHMN%X(~<8!aEa`vxRW-OGL`tI_7R<1hbsNcr-}Yy%BU_{xI`VqEX0a= z>~RYZ^04N;xXl_du4sR-hfd>rN#r7%D(qF@p(lr;C`&UZ6ZJL8>lrhZ@$jz=Jvta5 z3$`#DtE=~U+uhV%f7_l%coI;wIW?W;RlA$ZzOap=?21G$L||8#06^@QXU@Icv{FZz z*!s5{N19ZdeUn*VeSLeN&06_px!HK}DtG%F{Ex7=3_=8~TlmJ3SA}rSpiy+Y8sZP} zF9gIWqf^!%35&TJbEH%~cxS4mpMd<sfy=DK#c(ttd1q#^Hw*`PRF|ygN=4u_QO2#d zA+kaL#_>C()#%}e!CYXQ><R|sSSY#QDdTE*kBW1YEi9X5qeaMcNxawvc4^N*Co`uf zo105Z=A)8l<v}~Rn{1rR%Jgj}YU(?{cPnm}9?C{Q8mgUu{v%>S4~!-V94xky-m&K| zslT%Sgw4yHH6Gg2$#5u5<2Lc>1ddG{L6}_`DXV{zd}!MM$P{Zh47?iL@o1Dq%V-^l zlf4%i7ACt>c%Ou>L^=l)y#AdK({gK~N7k<IP0PbD76laF?feO)FeKz&&yo!{GhOTF z<`7u?sH(yvk)HH*L19ihy0<5=sgdBQurTGFnfp>qujr(#O+jYHFZhlWS3IgMdo}D# z7LBC?sF-dAYvMu})Oa*Ie(+?!8nz01<-n7pd5CFAatf;)jmk@ZHH`a&d<6ZKOA0i8 zUx;f|0qaV>Akf{<|CIw6gR$(iX3RmaybuuQO!ON!ik>`!@G?7L&bl1$fsbl{felZN z)*)uttMYn+Mb91LQ`VGk0-eU7%R+z|?x~9>CMnbwxF*1Z0qi*b2>*`q0@m=xN%Kw- z{8!VLi`kW81&ai)%Jnh#R$$4!Q0W;0Qj{1iLK#Ks@A?XtZsuNbLO(f_J=Wz|^9<Ln zMcOt{44C<_6<mnD38igH_~NVLk9Yf><3^1GeN`wutBFecF!sc$>A@6n?*i^owMNPy zkAm4Hh2ze)&9ifH!s@azV>T%qq?$w9u9_YR8crjVtP-VX#xK83C%Gx-be2<$ppClL zPsgJ@;~JG;JicX@?pEZP+j+XmSd?LJv@4cBIytxGS5Eis1a>m=LlPS=>S53{w@$VO zuJLrd=-QJPR|{n;>Yl!X0Ilaychd&N!Y{r4#L6MHZwegM(789ZwOb@M{T(-qFZx#u zTW7tvZ*e0iOrzd=mk!mt8+E|K_BR+aHaOrEXeps#c8)hs5%7Kug5Yd<10&g7J5(I8 zNv-nsFtcc(3&v$5rpF8_{#!DdEDrA{*23h&E>xxa_3-cbPz9_bzdPU+TZ;o>MIg*- zi&rz`$DQ)faXH9dJk@!cK*x0g_bb7(P4A__DQVPvstTJKu}ut|=);Suishf;B4!z7 zL@z2h8Rt!m50T`y{n(zG5eoY{VlDkUH;yz!t+cfmf5=Uc7BAZBD>U3QCmY||v2>V5 z8$CJ$6kI48v|>7?K`);~b~#Oefyng>KDW8-r8%2}Rg74{fikIa(m5h3#J(-2mf04^ z2U`IpYLYJk*$LuJC#WL<q^`*DfGPZp9S9zkwMt-aDh+LESS*}gatwy1U^~HTS&Clf zF#=n8YGh0OPs{HM051F5WeQfz@=SCrJjCT675|GR;f>d~Ej!)zv<;ti{(k{(Z$N<P zCJ12p{{b}?Ab@=x1Qa;^y9MCSNJ)5g^lc?SJ9A><Kl=99Ja}>9r=;cob7Au~eyY>e z^Qv_8jq@j7{|)pJ$*q79hAa-r_Za5GsmUGoBe26`_kXwkSK~F<`pbqH^C3S;2dOt9 zcof%TB;JVPW3b-%Dd0~JQNdePQ-O7JnGRd1Jp8G?{1marSj`BO*1rptWy}($iRd$| zX8(<sE~QyS2uhd1#t3&1@xe>*tg?!+_;`7JoGb?d^mnyw(!2KgeL1l4DzIcpz-kgM zzEc0NzJ+<Q?WU$S`2+cE|F-<8SCNnV%x`%F(~AA+H2*QuO9k+6g!q;$B6UXJrG+(A z9+u=?u#bvdaJ&eA8Ml4jCcE4HY8j<9{yz@##ls81f=F>>rqq9zN2a}-!k+>ty{mR< zI|4g5^UL6ggW7^K)*A!+^uKExU-4>#o+GobY(d`C5P|itdHptC8dK8Gkbu+m-$-}< zUz6Ko_F5{bPk%T(!fTWy*z`!=0<jw$r|sjrZeJN@<`?usT(k_qgTO!)c#XhHY#j;C zFRr@|fs)wzrcV3|1@B|<)`X#^0dk(hT{Yhh(6&w5jcS8YSmIzGjya*N_UX<7hQjI2 zX?G2@Z1<o}Y0LHeyu;Dc&x1Gp>He<io}cS5^+YPfKx>>fr3H~k&7TmEqzDv6_r+<@ zX7pJq(SH|R8*H4JFZ9$GSsrv3!e7Ps84_32=q;-{H{NW^-FU54%>Ema!i$KiIQImi z!ulXP+bz`3@l$`r3LNxZMURm?R@nqoJ{!BUf<XigU?|kRmlyB>qLDR$sRG1!eSeTp z@H<w4g?JX6m){FfMR)aX5YR{-kWNp*+6aB}yI{DeEjhspo}ZC!AN+H9?<PLQ$qtTn zJO;D#3ts?uk-2SRc;{^;S&~&Gf3sXJ_Dpr5G0<yPibVWl|L_{{AGev1r`)uRO0)hy z)JbT4a7UdB?SE6}H*+e?M@YI+dC<dcu~z>@)4e-((=$2IL>d2I-pCUF<_&|BFYwQ- zKnxmdsqe$IBEg>KU!b{*tPcuE!#X(x`OExPIc%+%2Y0t%gB>SWXK0`+zmX$?ocMvm zn0Pv<0vd$=WBAL*clCavPT>=V=-R^e+iO6kRQ%gv0A?pEX#hw(|NJsXnkdkI=PU&I zJHKJj(d^;7n1271UqQ!YV^E3%euf$-isp-ZN$pysJ_p+k0N>*H%=#VrH0=BudZ$&$ z9O8c!<0nW3$!;bi{QF3GNb^QxB%X+pW8g(#uESSUW^lM{#$R*2$P9ty&zP}vzIPQb z?-~2Y9;-}arkRk3NPzbfF(m(G&6KoWURXg@g|W4rU^OA=|F=Cfg=N>~&rJC;kB_mM z#EU4w@*md!^j3e*@L$dS?hL?8o8s7_$jHJWVk8J`kudZJle`lZY>|>(Jqzw&ABLHB zoN!a-fK5cHba~I|ZWf`K*kGo0qpBVRTwk3<`Tbn_bZ^9P2fkQ?eMxz&uoW?qC#k>O zIlTD@5>WS9p;gA=4GGmBCH;?_f?uff=#EnqK(xod^wj@uj%%p7!4xe%)9CWDkiYZn zPdXit9tMCZH1e~u@h+48E5v_QJm%iEC(QiEeNZ$~TZr_;)cnr9awxbxO54cW&{?gq zPSL={u|f_212(=$BhdA;;E^oJDViec?RF6bgTrg+?=1dP95;(!k6mQ)9wS|jI(mRo z>`w=-1qhV&U|#Q7_Jc?IA!6Tr-{exUt23kd2GoJhC%N;XEjCf1Iv-}Iga1#^O)ut` z`29EVGJ{6$XZ=~f6%AYfEkp5Er#%R$ZhC)Kr9Vqk3sLCuB>7#I_+`$4^>TRy?zn{q z2K^Z6q+_Aax7v^Ga^E*+8R~u~aR01)x9*5(i)?`l#7#E*_d2omAJtKBef8n@dewSw z?`{nP{<o(4oq3IbvbklMX0!~73b>wu0+I^~Z6QT8pYPG?nV8?No8k3oG2mwX*WcSu zTqjgiJ2<gXzZOdiB1G*8i0K$u3uBfGNF$!f!Lbwr>O_Y#+u!xD_c6qD53iPRc!`<~ zZXT6-1L;fU1No<)mdXH8U6&<?oi%0op~>P_6FK<~Gb>3FRvcaxiizZOJyyc{?XLmR z>80<andfRHLZGE@rP)nG2w~AR4<)Z6HA8OP?OKams7`_{@W*;vYWZ@Z8?S?7S&QlT z(2D6kVXau%5N1`XKsvwRO6AG3FiF=7piGTj15|P0;(>}!MQMfj1rjr%bioM^e&0Dv z_89*7%I?0h|9UlM#6oUK0`TREQe=+|w}?@?NIt?Qo&I5%Hh#8ttVT?W^~O5`hT|#p zGIX6PTk1M@$?^}$WAz7ib*7cQDiKWlw8kbL*L5%BNLA~ea2tg<=h0K_m{>u(Vw@H7 zHfl@#fTsDA`5&<q9!K9?o1bqs^{CZ(ZSC5Kl0~0wntNYe?(FVZL!jHvz_X1-rSyT+ z;^G?~wlkvZVqJ>yYwu?pO?G04)lVYlk<gXiFp0P!At21f$1nibM5Mfnm?&xpkFaZI zU+3|a!qD30@nRX3#Fn)`u6_~ZK277E75*iV0sFc7TGTjyX{!%rk>>vX#>sncu(te# zsM+|J0nWXUadQ%%BbT(nqtlzlL3@EcQL^CGi4vUrdj1gedOy2)FOSE=9oMe)(c_nE z>(0*}4P2gqCl{xId4cbn9Rlo|TDv7GdtO*CKcyPE><(ITq-@kxaSm>uIrsoCbqN9T zCjD?j48u#(gTV;f@LxU7d8?W$xY$OB*(~WCxLicv`+G;t5Bt}W!jj3rJ>l#Lx^l*3 zIL~ylP3eRVcY0ZHxGHhOlH}KtdRcVZb>%Ar!n(rN2v|aa2Nd$uvA!>xCFux@ybljc z-ZNzX`K8JJ`!|%4Iy{s*JJeG*y-`)bCc0W0E)9R+kP_Q<qbb2ku)v|=qWw4mi3TH* zk*wd!OhW}XoY}n)SBef_(vGLnx@$?R)0(mB-FJ1F!*FHBs>1K4s0&J{hbFZwz`hx> zmj<4+N^D3=D2&qPT_O76+In9^Kh?0e;9<6NXg?Km$P+KY{cPRQk6YS2%$M3B5%W|p z-04H!A~`XJesQ<p*M+52(f7kTO{;|I!!sEDvd`&qdxpXn0&puXkWNv|+C>ZXOwAA5 zE3{MiDhgK2X0+mNdPI_FR=T|30Ablu1Bt5&5ezD(1X0g<bi%N#i?Vs7Mjyy<v%ZAH z^7Obzm!~;#KMcB+eUZy%v8IN(7O6O%T|IicWeb1g{U9pzp%hiY+&HcPosK<nbDc@# zxrvXVf49rZ?U|y_FXqKtkZJ$-)FhGpgtjsw0z!3092X)m&Xl+JxsP00q9Xg8Fn*qG zCYLJA{pKuxPlshbAf`|7Y(Vh6S#fhofCHxv53?L?xCTNvuVLZqXyXAD&j_U5!WFnZ z{#qe&gihDGOt))43G#6M=*u(&QqTA*rg6bJ!2+#Fw)wZYe1{L$yFPNo3jsU^B}`Yj z2A>t4a8#lIa)NI7XP?#8q+P|G`KV6b7u`p&d~6e&nbt*1eLlVWS>DI}rMiVGlVO2x zfz*OvYPCk%f)5M5>HH^+#>Xb-*C=<dCwlN)D(ljSOI)T~zyE>ybaFQSbD8Hv!YH<o zX;-=D!IsXg{V>ScTCFkqSC7TnFs=81X(5j&its*_z<A}DhL=If&jBpvcmXOZg?3MU zdRik^E$5@rkaO<k5OR(+s(!K1wU3djZ#_k5$Ps0N)kGB!kQ2}nXa4aWiQ9>)h${&K zNkWJy2`)+Wq<?=M8zQ*5^q)Tg3GgJAC;$8C>fZ<r-Gr+o`X_$>+MW8h83DoG65OjQ xjkFS#5CElq@?S-Hh=73oPZ&Z`Y(g6d_)oa+WC!P80Of>)Ur?HR!%25{{|CWNBxwKu delta 28738 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 diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 4ce96351..ab937cc4 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -25,7 +25,7 @@ protein-gobp|:;;GO_|class-class|RO_0000056|http://purl.obolibrary.org/obo/|http: protein-gocc|:;;GO_|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein protein-gomf|:;;GO_|class-class|RO_0000085|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein protein-pathway|;;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens -protein-protein|9606.;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|2;>=;700|None +protein-protein|9606.;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|None|None rna-anatomy|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy rna-cell|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line rna-protein|;;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding From 8977722003452e7bc0c3aa66286efd03c6aa0c52 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 13 Dec 2021 17:40:14 -0700 Subject: [PATCH 038/112] rna-protein --- resources/pheknowlator_source_metadata.xlsx | Bin 39145 -> 39378 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx index 2af158d09ccf16188f09df8d20ca93d51fc513e7..f8d068144c89a34c43155fae2d07cfafc2afdfe2 100644 GIT binary patch delta 7343 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 delta 7104 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 SB><mbu<}eAl&RGJi~bArd5UQO From eaa97e3e082db0d4fbde52c06c74918ce800a7dc Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 14 Dec 2021 14:20:00 -0700 Subject: [PATCH 039/112] adding code to ignore file header metadata --- pkt_kg/downloads.py | 3 ++- pkt_kg/edge_list.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkt_kg/downloads.py b/pkt_kg/downloads.py index 7bc5d27d..e82b6dd2 100644 --- a/pkt_kg/downloads.py +++ b/pkt_kg/downloads.py @@ -80,7 +80,8 @@ def __init__(self, data_path: str, resource_data: Optional[str] = None) -> None: raise TypeError(log_str) else: resource_data_file: TextIO = open(self.resource_data) - self.resource_info: List = resource_data_file.read().splitlines(); resource_data_file.close() + self.resource_info: List = [x for x in resource_data_file.read().splitlines() if not x.startswith('#')] + resource_data_file.close() self.resource_dict: Dict[str, List[str]] = {} self.source_list: Dict[str, str] = {} diff --git a/pkt_kg/edge_list.py b/pkt_kg/edge_list.py index d3b2d973..c4245516 100755 --- a/pkt_kg/edge_list.py +++ b/pkt_kg/edge_list.py @@ -51,7 +51,7 @@ def __init__(self, data_files: Dict[str, str], source_file: str) -> None: self.source_info: Dict[str, Dict[str, Any]] = dict() with open(source_file, 'r') as source_file_data: - for row in source_file_data.read().splitlines(): + for row in [x for x in source_file_data.read().splitlines() if not x.startswith('#')]: cols = ['"{}"'.format(x.strip()) for x in list(csv.reader([row], delimiter='|', quotechar='"'))[0]] key = cols[0].strip('"').strip("'") self.source_info[key] = {} From 8a44ad47760452586c9e31d7a43a5423957fc599 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 14 Dec 2021 14:20:14 -0700 Subject: [PATCH 040/112] updating clinvar data file --- builds/data_to_download.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builds/data_to_download.txt b/builds/data_to_download.txt index 2b9728bb..b2029b1c 100755 --- a/builds/data_to_download.txt +++ b/builds/data_to_download.txt @@ -38,7 +38,7 @@ genomic_sequence_ontology_mappings.xlsx, https://storage.googleapis.com/pheknowl # protein ontology consortium sparql query results human_pro_classes.html, https://sparql.proconsortium.org/virtuoso/sparql?query=PREFIX+obo%3A+%3Chttp%3A%2F%2Fpurl.obolibrary.org%2Fobo%2F%3E%0D%0A%0D%0ASELECT+%3FPRO_term%0D%0AFROM+%3Chttp%3A%2F%2Fpurl.obolibrary.org%2Fobo%2Fpr%3E%0D%0AWHERE+%7B%0D%0A+++++++%3FPRO_term+rdf%3Atype+owl%3AClass+.%0D%0A+++++++%3FPRO_term+rdfs%3AsubClassOf+%3Frestriction+.%0D%0A+++++++%3Frestriction+owl%3AonProperty+obo%3ARO_0002160+.%0D%0A+++++++%3Frestriction+owl%3AsomeValuesFrom+obo%3ANCBITaxon_9606+.%0D%0A%0D%0A+++++++%23+use+this+to+filter-out+things+like+hgnc+ids%0D%0A+++++++FILTER+%28regex%28%3FPRO_term%2C%22http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2F*%22%29%29+.%0D%0A%7D&format=text%2Fhtml&debug= # clinvar variant-diseases and phenotypes -variant_summary.txt, ftp://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz +ClinVarFullRelease.xml, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/xml/ClinVarFullRelease_00-latest.xml.gz # uniprot protein-cofactor and protein-catalyst uniprot-cofactor-catalyst.tab, https://www.uniprot.org/uniprot/?query=&fil=organism%3A%22Homo%20sapiens%20(Human)%20%5B9606%5D%22&columns=id%2Creviewed%2Centry%20name%2Cdatabase(PRO)%2Cchebi(Cofactor)%2Cchebi(Catalytic%20activity)&format=tab From 9d709814bde0acb00cdd03aa981af84e323c1501 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Thu, 23 Dec 2021 15:08:04 -0700 Subject: [PATCH 041/112] addressing unionOf bug --- pkt_kg/owlnets.py | 87 ++++++++++++++++++++++++++----------------- tests/test_owlnets.py | 59 +++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 43 deletions(-) diff --git a/pkt_kg/owlnets.py b/pkt_kg/owlnets.py index 499c74c5..f9caba57 100644 --- a/pkt_kg/owlnets.py +++ b/pkt_kg/owlnets.py @@ -429,10 +429,10 @@ def returns_object_property(sub: URIRef, obj: URIRef, prop: URIRef = None) -> UR """Checks the subject and object node types in order to determine the correct type of owl:ObjectProperty. The following ObjectProperties are returned for each of the following subject-object types: - - subject + object are not PATO terms + prop is None --> rdfs:subClassOf - - sub + obj are PATO terms + prop is None --> rdfs:subClassOf - - sub is not a PATO term, but obj is a PATO term --> owl:RO_000086 - - sub is a PATO term + obj is a PATO term + prop is not None --> prop + - if sub + obj are PATO terms + prop is None --> rdfs:subClassOf + - elif sub is not a PATO term, but obj is a PATO term --> obo:RO_000086 + - elif prop is not None --> prop + - else --> rdfs:subClassOf Args: sub: An rdflib.term object. @@ -444,8 +444,8 @@ def returns_object_property(sub: URIRef, obj: URIRef, prop: URIRef = None) -> UR """ if ('PATO' in sub and 'PATO' in obj) and not prop: return RDFS.subClassOf - elif ('PATO' not in sub and 'PATO' not in obj) and not prop: return RDFS.subClassOf - elif 'PATO' not in sub and 'PATO' in obj: return URIRef(obo + 'RO_0000086') + elif 'PATO' not in sub and 'PATO' in obj: return obo.RO_0000086 + elif not prop: return RDFS.subClassOf else: return prop @staticmethod @@ -499,20 +499,7 @@ def parses_constructors(self, node: URIRef, edges: Dict, class_dict: Dict, relat -> Tuple[Set, Optional[Dict]]: """Traverses a dictionary of rdflib objects used in the owl:unionOf or owl:intersectionOf constructors, from which the original set of edges used to the construct the class_node are edited, such that all owl-encoded - information is removed. For example: - INPUT: <!-- http://purl.obolibrary.org/obo/CL_0000995 --> - <owl:Class rdf:about="http://purl.obolibrary.org/obo/CL_0000995"> - <owl:equivalentClass> - <owl:Class> - <owl:unionOf rdf:parseType="Collection"> - <rdf:Description rdf:about="http://purl.obolibrary.org/obo/CL_0001021"/> - <rdf:Description rdf:about="http://purl.obolibrary.org/obo/CL_0001026"/> - </owl:unionOf> - </owl:Class> - </owl:equivalentClass> - <rdfs:subClassOf rdf:resource="http://purl.obolibrary.org/obo/CL_0001060"/> - </owl:Class> - OUTPUT: [(CL_0000995, rdfs:subClassOf, CL_0001021), (CL_0000995, rdfs:subClassOf, CL_0001026)] + information is removed. See examples here: https://github.com/callahantiff/PheKnowLator/wiki/OWL-NETS-2.0. Args: node: An rdflib term of type URIRef or BNode that references an OWL-encoded class. @@ -526,21 +513,24 @@ def parses_constructors(self, node: URIRef, edges: Dict, class_dict: Dict, relat """ cleaned: Set = set() - if 'unionOf' in edges.keys() or 'intersectionOf' in edges.keys(): - batch = class_dict[edges['unionOf' if 'unionOf' in edges.keys() else 'intersectionOf']] - else: batch = edges + if 'unionOf' in edges.keys(): batch = class_dict[edges['unionOf']]; keyword = 'union' + elif 'intersectionOf' in edges.keys(): batch = class_dict[edges['intersectionOf']]; keyword = 'intersection' + else: batch = edges; keyword = 'other' while batch: if ('first' in batch.keys() and 'rest' in batch.keys()) and 'type' not in batch.keys(): if isinstance(batch['first'], URIRef) and isinstance(batch['rest'], BNode): obj_property = self.returns_object_property(node, batch['first'], relation) if node != batch['first']: - cleaned |= {(node, obj_property, batch['first'])} + if keyword == 'union': cleaned |= {(batch['first'], obj_property, node)} + else: cleaned |= {(node, obj_property, batch['first'])} batch = class_dict[batch['rest']] if 'rest' in batch.keys() else None else: batch = class_dict[batch['rest']] elif isinstance(batch['first'], URIRef) and isinstance(batch['rest'], URIRef): obj_property = self.returns_object_property(node, batch['first'], relation) - cleaned |= {(node, obj_property, batch['first'])}; batch = None + if keyword == 'union': cleaned |= {(batch['first'], obj_property, node)} + else: cleaned |= {(node, obj_property, batch['first'])} + batch = None else: batch = self.parses_anonymous_axioms(batch, class_dict) else: break @@ -595,6 +585,36 @@ class (referenced by node) in order to remove owl-encoded information. An exampl return cleaned, results[1] else: return cleaned, axioms + @staticmethod + def verifies_cleaned_classes(cleaned_classes: Set) -> Set: + """Verifies a set of cleaned tuples to ensure that there are not duplicate triples (i.e., subject-object + pairs with different properties). The function assumes that a duplicate tuple will include RDFS.subClassOf, + which should be removed. + + Args: + cleaned_classes: A set of tuples, where each tuple contains three URIRef objects. + + Returns: + A set of tuples, where each tuple contains a cleaned triple comprised of three URIRef objects. + """ + + org = len([x[0::2] for x in list(cleaned_classes)]) + unq = len(set([x[0::2] for x in list(cleaned_classes)])) + + if org == unq: return cleaned_classes + else: + cleaned_dict: Dict = dict(); verified_classes: Set = set() + for s, p, o in cleaned_classes: + key = '{}--{}'.format(str(s), str(o)) + if key in cleaned_dict.keys(): cleaned_dict[key] += [str(p)] + else: cleaned_dict[key] = [str(p)] + for k, v in cleaned_dict.items(): + s = URIRef(k.split('--')[0]); o = URIRef(k.split('--')[1]) + if len(v) > 1 and str(RDFS.subClassOf) in v: p = URIRef([x for x in v if x != str(RDFS.subClassOf)][0]) + else: p = URIRef(v[0]) + verified_classes |= {(s, p, o)} + return verified_classes + def cleans_owl_encoded_entities(self, node_list: List, verbose: bool = True) -> None: """Loops over a all owl:Class and owl: Axiom objects and decodes the OWL semantics returning the corresponding triples for each type without OWL semantics. @@ -620,8 +640,9 @@ def cleans_owl_encoded_entities(self, node_list: List, verbose: bool = True) -> if not neg and not comp: node, org = (node_info[0], node) if isinstance(node, BNode) else (node, node) cleaned_entities |= {org}; cleaned_classes: Set = set() - bnodes = set(x for x in self.graph.objects(org) if isinstance(x, BNode)) - for element in (bnodes if len(bnodes) > 0 else node_info[1].keys()): + # bnodes = set(x for x in self.graph.objects(org) if isinstance(x, BNode)) + # for element in (bnodes if len(bnodes) > 1 else node_info[1].keys()): + for element in node_info[1].keys(): edges = node_info[1][element] while edges: if 'subClassOf' in edges.keys(): @@ -636,11 +657,12 @@ def cleans_owl_encoded_entities(self, node_list: List, verbose: bool = True) -> results = self.parses_restrictions(node, edges, node_info[1]) if results is not None: cleaned_classes |= results[0]; edges = results[1] else: edges = None - else: # catch all other axioms -- only catching owl:onProperty + else: # catch all other axioms -- currently only catching owl:onProperty misc = [x for x in edges.keys() if x not in ['type', 'first', 'rest', 'onProperty']] edges = None; self.owl_nets_dict['misc'][n3(node)] = {tuple(misc)} - decoded_graph = adds_edges_to_graph(decoded_graph, list(cleaned_classes), False) - self.owl_nets_dict['decoded_entities'][n3(node)] = cleaned_classes + verified_classes = self.verifies_cleaned_classes(cleaned_classes) + decoded_graph = adds_edges_to_graph(decoded_graph, list(verified_classes), False) + self.owl_nets_dict['decoded_entities'][n3(node)] = verified_classes self.graph = decoded_graph; self.graph = self.cleans_decoded_graph(verbose) # ; pbar.close() return None @@ -667,7 +689,6 @@ def makes_graph_connected(self, graph: Graph, common_ancestor: Union[URIRef, str log_str = 'Obtaining node list'; print(log_str); logger.info(log_str) anc_node, roots = common_ancestor if isinstance(common_ancestor, URIRef) else URIRef(common_ancestor), set() nodes = set([x for x in tqdm(list(graph.subjects()) + list(graph.objects())) if isinstance(x, URIRef)]) - print('Identifying root nodes') for x in tqdm(nodes): ancs = gets_entity_ancestors(graph, [x], RDFS.subClassOf) @@ -679,7 +700,6 @@ def makes_graph_connected(self, graph: Graph, common_ancestor: Union[URIRef, str try: ancs = [mode(ancs)] except StatisticsError: ancs = sample(ancs, 1) if not any(x for x in ancs if x in roots) else [] roots |= {ancs[0]} if len(ancs) > 0 else {x} - log_str = 'Updating graph connectivity'; print(log_str); logger.info(log_str) rel = RDF.type if self.kg_construct_approach == 'instance' else RDFS.subClassOf needed_triples = set((URIRef(x), rel, anc_node) for x in roots if x != anc_node) @@ -701,13 +721,10 @@ def purifies_graph_build(self, graph: Graph) -> Graph: """ log_str = 'Purifying Graph Based on Construction Approach'; logger.info(log_str); print(log_str) - org_rel = RDF.type if self.kg_construct_approach == 'subclass' else RDFS.subClassOf pure_rel = RDFS.subClassOf if org_rel == RDF.type else RDF.type - log_str = 'Determining what triples need purification'; print(log_str); logger.info(log_str) triples = list(graph.triples((None, org_rel, None))) - log_str = 'Processing {} {} triples'.format(len(triples), org_rel); print(log_str); logger.info(log_str) for edge in tqdm(triples): graph.add((edge[0], pure_rel, edge[2])); graph.remove(edge) diff --git a/tests/test_owlnets.py b/tests/test_owlnets.py index 2025348c..e8a5202a 100644 --- a/tests/test_owlnets.py +++ b/tests/test_owlnets.py @@ -47,18 +47,23 @@ def setUp(self): # set-up input arguments self.write_location = self.dir_loc_resources + '/knowledge_graphs' self.kg_filename = '/so_with_imports.owl' + self.kg_filename2 = '/clo_with_imports.owl' # read in knowledge graph self.graph = Graph().parse(self.dir_loc_resources + '/knowledge_graphs/so_with_imports.owl', format='xml') + self.graph2 = Graph().parse('http://purl.obolibrary.org/obo/clo.owl', format='xml') # initialize class self.owl_nets = OwlNets(kg_construct_approach='subclass', graph=self.graph, write_location=self.write_location, filename=self.kg_filename) self.owl_nets2 = OwlNets(kg_construct_approach='instance', graph=self.graph, write_location=self.write_location, filename=self.kg_filename) + self.owl_nets3 = OwlNets(kg_construct_approach='subclass', graph=self.graph2, + write_location=self.write_location, filename=self.kg_filename2) # update class attributes dir_loc_owltools = os.path.join(current_directory, 'utils/owltools') self.owl_nets.owl_tools = os.path.abspath(dir_loc_owltools) self.owl_nets2.owl_tools = os.path.abspath(dir_loc_owltools) + self.owl_nets3.owl_tools = os.path.abspath(dir_loc_owltools) return None @@ -433,17 +438,17 @@ def test_returns_object_property(self): """Tests the returns_object_property method.""" # when sub and obj are PATO terms and property is none - res1 = self.owl_nets.returns_object_property(obo.PATO_0001199, obo.PATO_0000402, None) + res1 = self.owl_nets.returns_object_property(obo.PATO_0001199, obo.PATO_0000402) self.assertIsInstance(res1, URIRef) self.assertEqual(res1, RDFS.subClassOf) # when sub and obj are NOT PATO terms and property is none - res2 = self.owl_nets.returns_object_property(obo.SO_0000784, obo.GO_2000380, None) + res2 = self.owl_nets.returns_object_property(obo.SO_0000784, obo.GO_2000380) self.assertIsInstance(res2, URIRef) self.assertEqual(res2, RDFS.subClassOf) # when the obj is a PATO term and property is none - res3 = self.owl_nets.returns_object_property(obo.SO_0000784, obo.PATO_0001199, None) + res3 = self.owl_nets.returns_object_property(obo.SO_0000784, obo.PATO_0001199) self.assertIsInstance(res3, URIRef) self.assertEqual(res3, obo.RO_0000086) @@ -458,7 +463,7 @@ def test_returns_object_property(self): self.assertEqual(res5, obo.RO_0002202) # when sub is a PATO term and property is none - res6 = self.owl_nets.returns_object_property(obo.PATO_0001199, obo.SO_0000784, None) + res6 = self.owl_nets.returns_object_property(obo.PATO_0001199, obo.SO_0000784) self.assertEqual(res6, None) return None @@ -526,7 +531,7 @@ def test_parses_constructors_intersection(self): # set-up inputs node = obo.SO_0000034 node_info = self.owl_nets.creates_edge_dictionary(node) - bnodes = set(x for x in self.owl_nets.graph.objects(node, None) if isinstance(x, BNode)) + bnodes = set(x for x in self.owl_nets.graph.objects(node) if isinstance(x, BNode)) edges = {k: v for k, v in node_info[1].items() if 'intersectionOf' in v.keys() and k in bnodes} edges = node_info[1][list(x for x in bnodes if x in edges.keys())[0]] @@ -539,12 +544,12 @@ def test_parses_constructors_intersection(self): return None def test_parses_constructors_intersection2(self): - """Tests the parses_constructors method for the UnionOf class constructor""" + """Tests the parses_constructors method for the intersectionOf class constructor""" # set-up inputs node = obo.SO_0000078 node_info = self.owl_nets.creates_edge_dictionary(node) - bnodes = set(x for x in self.owl_nets.graph.objects(node, None) if isinstance(x, BNode)) + bnodes = set(x for x in self.owl_nets.graph.objects(node) if isinstance(x, BNode)) edges = {k: v for k, v in node_info[1].items() if 'intersectionOf' in v.keys() and k in bnodes} edges = node_info[1][list(x for x in bnodes if x in edges.keys())[0]] @@ -556,13 +561,33 @@ def test_parses_constructors_intersection2(self): return None + def test_parses_constructors_union(self): + """Tests the parses_constructors method for the unionOf class constructor""" + + # set-up inputs + node = obo.CL_0000995 + node_info = self.owl_nets3.creates_edge_dictionary(node) + bnodes = set(x for x in self.owl_nets3.graph.objects(node) if isinstance(x, BNode)) + edges = {k: v for k, v in node_info[1].items() if 'unionOf' in v.keys() and k in bnodes} + edges = node_info[1][list(x for x in bnodes if x in edges.keys())[0]] + + # test method + res = self.owl_nets3.parses_constructors(node, edges, node_info[1]) + self.assertIsInstance(res, Tuple) + self.assertEqual(sorted(list(res[0])), + [(obo.CL_0001021, RDFS.subClassOf, obo.CL_0000995), + (obo.CL_0001026, RDFS.subClassOf, obo.CL_0000995)]) + self.assertEqual(res[1], None) + + return None + def test_parses_restrictions(self): """Tests the parses_restrictions method.""" # set-up inputs node = obo.SO_0000078 node_info = self.owl_nets.creates_edge_dictionary(node) - bnodes = set(x for x in self.owl_nets.graph.objects(node, None) if isinstance(x, BNode)) + bnodes = set(x for x in self.owl_nets.graph.objects(node) if isinstance(x, BNode)) edges = {k: v for k, v in node_info[1].items() if ('type' in v.keys() and v['type'] == OWL.Restriction) and k in bnodes} edges = node_info[1][list(x for x in bnodes if x in edges.keys())[0]] @@ -576,6 +601,24 @@ def test_parses_restrictions(self): return None + def test_verifies_cleaned_classes(self): + """Tests the verifies_cleaned_classes method""" + + # create input data + cleaned_classes = {(obo.HP_0000602, obo.BFO_0000051, obo.HP_0000597), + (obo.HP_0000602, RDFS.subClassOf, obo.HP_0000597), + (obo.HP_0007715, RDFS.subClassOf, obo.HP_0000597), + (obo.HP_0007715, obo.BFO_0000051, obo.HP_0000597)} + cleaned_result = sorted(list({(obo.HP_0000602, obo.BFO_0000051, obo.HP_0000597), + (obo.HP_0007715, obo.BFO_0000051, obo.HP_0000597)})) + + # test method + verified_classes = self.owl_nets3.verifies_cleaned_classes(cleaned_classes) + self.assertIsInstance(verified_classes, Set) + self.assertEqual(sorted(list(verified_classes)), cleaned_result) + + return None + def test_cleans_owl_encoded_entities(self): """Tests the cleans_owl_encoded_entities method""" From 0911f0e1147da9fc121acbf0f7a1719607fa0d81 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Thu, 23 Dec 2021 17:38:38 -0700 Subject: [PATCH 042/112] improved testing dependencies --- tests/test_owlnets.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/test_owlnets.py b/tests/test_owlnets.py index e8a5202a..a884a096 100644 --- a/tests/test_owlnets.py +++ b/tests/test_owlnets.py @@ -47,23 +47,18 @@ def setUp(self): # set-up input arguments self.write_location = self.dir_loc_resources + '/knowledge_graphs' self.kg_filename = '/so_with_imports.owl' - self.kg_filename2 = '/clo_with_imports.owl' # read in knowledge graph self.graph = Graph().parse(self.dir_loc_resources + '/knowledge_graphs/so_with_imports.owl', format='xml') - self.graph2 = Graph().parse('http://purl.obolibrary.org/obo/clo.owl', format='xml') # initialize class self.owl_nets = OwlNets(kg_construct_approach='subclass', graph=self.graph, write_location=self.write_location, filename=self.kg_filename) self.owl_nets2 = OwlNets(kg_construct_approach='instance', graph=self.graph, write_location=self.write_location, filename=self.kg_filename) - self.owl_nets3 = OwlNets(kg_construct_approach='subclass', graph=self.graph2, - write_location=self.write_location, filename=self.kg_filename2) # update class attributes dir_loc_owltools = os.path.join(current_directory, 'utils/owltools') self.owl_nets.owl_tools = os.path.abspath(dir_loc_owltools) self.owl_nets2.owl_tools = os.path.abspath(dir_loc_owltools) - self.owl_nets3.owl_tools = os.path.abspath(dir_loc_owltools) return None @@ -464,7 +459,7 @@ def test_returns_object_property(self): # when sub is a PATO term and property is none res6 = self.owl_nets.returns_object_property(obo.PATO_0001199, obo.SO_0000784) - self.assertEqual(res6, None) + self.assertEqual(res6, RDFS.subClassOf) return None @@ -564,6 +559,14 @@ def test_parses_constructors_intersection2(self): def test_parses_constructors_union(self): """Tests the parses_constructors method for the unionOf class constructor""" + # instantiate class + self.kg_filename2 = '/clo_with_imports.owl' + self.graph2 = Graph().parse('http://purl.obolibrary.org/obo/clo.owl', format='xml') + self.owl_nets3 = OwlNets(kg_construct_approach='subclass', graph=self.graph2, + write_location=self.write_location, filename=self.kg_filename2) + dir_loc_owltools = os.path.join(os.path.dirname(__file__), 'utils/owltools') + self.owl_nets3.owl_tools = os.path.abspath(dir_loc_owltools) + # set-up inputs node = obo.CL_0000995 node_info = self.owl_nets3.creates_edge_dictionary(node) @@ -613,7 +616,7 @@ def test_verifies_cleaned_classes(self): (obo.HP_0007715, obo.BFO_0000051, obo.HP_0000597)})) # test method - verified_classes = self.owl_nets3.verifies_cleaned_classes(cleaned_classes) + verified_classes = self.owl_nets.verifies_cleaned_classes(cleaned_classes) self.assertIsInstance(verified_classes, Set) self.assertEqual(sorted(list(verified_classes)), cleaned_result) From 7e5a1cb8469fbc129cf13d663025489c57d9a427 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Thu, 23 Dec 2021 17:38:46 -0700 Subject: [PATCH 043/112] improving logic --- pkt_kg/owlnets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkt_kg/owlnets.py b/pkt_kg/owlnets.py index f9caba57..ea6c2274 100644 --- a/pkt_kg/owlnets.py +++ b/pkt_kg/owlnets.py @@ -425,7 +425,7 @@ def detects_complement_of_constructed_classes(self, node_info: Dict, node: URIRe else: return False @staticmethod - def returns_object_property(sub: URIRef, obj: URIRef, prop: URIRef = None) -> URIRef: + def returns_object_property(sub: URIRef, obj: URIRef, prop: Optional[URIRef] = None) -> URIRef: """Checks the subject and object node types in order to determine the correct type of owl:ObjectProperty. The following ObjectProperties are returned for each of the following subject-object types: @@ -443,9 +443,9 @@ def returns_object_property(sub: URIRef, obj: URIRef, prop: URIRef = None) -> UR An rdflib.term object that represents an owl:ObjectProperty. """ - if ('PATO' in sub and 'PATO' in obj) and not prop: return RDFS.subClassOf + if ('PATO' in sub and 'PATO' in obj) and prop is None: return RDFS.subClassOf elif 'PATO' not in sub and 'PATO' in obj: return obo.RO_0000086 - elif not prop: return RDFS.subClassOf + elif prop is None: return RDFS.subClassOf else: return prop @staticmethod From f0a196d8adf6f08c6f302dd8972534a7c61f114a Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Thu, 23 Dec 2021 17:47:23 -0700 Subject: [PATCH 044/112] partially complete clinvar changes --- builds/data_preprocessing.py | 62 +- notebooks/Data_Preparation.ipynb | 1592 +++++++++++++++++++++++++++++- 2 files changed, 1579 insertions(+), 75 deletions(-) diff --git a/builds/data_preprocessing.py b/builds/data_preprocessing.py index 27f1697e..922dcd4c 100755 --- a/builds/data_preprocessing.py +++ b/builds/data_preprocessing.py @@ -138,7 +138,7 @@ def _preprocess_hgnc_data(self) -> pandas.DataFrame: 'name', 'location', 'alias_name']] hgnc.rename(columns={'uniprot_ids': 'uniprot_id', 'location': 'map_location', 'locus_type': 'hgnc_gene_type'}, inplace=True) - hgnc['hgnc_id'].replace('.*\:', '', inplace=True, regex=True) # strip 'HGNC' off of the identifiers + hgnc['hgnc_id'].str.replace('.*\:', '', inplace=True, regex=True) # strip 'HGNC' off of the identifiers hgnc.fillna('None', inplace=True) # replace NaN with 'None' hgnc['entrez_id'] = hgnc['entrez_id'].apply(lambda x: str(int(x)) if x != 'None' else 'None') # make col str # combine certain columns into single column @@ -150,12 +150,13 @@ def _preprocess_hgnc_data(self) -> pandas.DataFrame: 'name', 'synonyms'], '|') # reformat hgnc gene type for v in self.genomic_type_mapper['hgnc_gene_type'].keys(): - explode_df_hgnc['hgnc_gene_type'].replace(v, self.genomic_type_mapper['hgnc_gene_type'][v], inplace=True) + explode_df_hgnc['hgnc_gene_type'].str.replace(v, self.genomic_type_mapper['hgnc_gene_type'][v], + inplace=True) # reformat master hgnc gene type explode_df_hgnc['master_gene_type'] = explode_df_hgnc['hgnc_gene_type'] master_dict = self.genomic_type_mapper['hgnc_master_gene_type'] for val in master_dict.keys(): - explode_df_hgnc['master_gene_type'].replace(val, master_dict[val], inplace=True) + explode_df_hgnc['master_gene_type'].str.replace(val, master_dict[val], inplace=True) # post-process reformatted data explode_df_hgnc.drop(['alias_symbol', 'alias_name'], axis=1, inplace=True) # remove original gene type column explode_df_hgnc.drop_duplicates(inplace=True) @@ -189,16 +190,17 @@ def _preprocess_ensembl_data(self) -> pandas.DataFrame: 'ensembl_gene_type', 'transcript_name', 'ensembl_transcript_type']) # reformat ensembl gene type gene_dict = self.genomic_type_mapper['ensembl_gene_type'] - for val in gene_dict.keys(): ensembl_geneset['ensembl_gene_type'].replace(val, gene_dict[val], inplace=True) + for val in gene_dict.keys(): ensembl_geneset['ensembl_gene_type'].str.replace(val, gene_dict[val], inplace=True) # reformat master gene type ensembl_geneset['master_gene_type'] = ensembl_geneset['ensembl_gene_type'] gene_dict = self.genomic_type_mapper['ensembl_master_gene_type'] - for val in gene_dict.keys(): ensembl_geneset['master_gene_type'].replace(val, gene_dict[val], inplace=True) + for val in gene_dict.keys(): ensembl_geneset['master_gene_type'].str.replace(val, gene_dict[val], inplace=True) # reformat master transcript type - ensembl_geneset['ensembl_transcript_type'].replace('vault_RNA', 'vaultRNA', inplace=True, regex=False) + ensembl_geneset['ensembl_transcript_type'].str.replace('vault_RNA', 'vaultRNA', inplace=True, regex=False) ensembl_geneset['master_transcript_type'] = ensembl_geneset['ensembl_transcript_type'] trans_d = self.genomic_type_mapper['ensembl_master_transcript_type'] - for val in trans_d.keys(): ensembl_geneset['master_transcript_type'].replace(val, trans_d[val], inplace=True) + for val in trans_d.keys(): + ensembl_geneset['master_transcript_type'].str.replace(val, trans_d[val], inplace=True) # post-process reformatted data ensembl_geneset.drop_duplicates(inplace=True) @@ -279,7 +281,7 @@ def _preprocess_uniprot_data(self) -> pandas.DataFrame: # explode nested data and perform light value reformatting explode_df_uniprot = explodes_data(uniprot.copy(), ['transcript_stable_id', 'entrez_id', 'hgnc_id'], ';') explode_df_uniprot = explodes_data(explode_df_uniprot.copy(), ['symbol', 'synonyms'], '|') - explode_df_uniprot['transcript_stable_id'].replace('\s.*', '', inplace=True, regex=True) # strip uniprot names + explode_df_uniprot['transcript_stable_id'].str.replace('\s.*', '', inplace=True, regex=True) # strip uniprot explode_df_uniprot.drop(['Status'], axis=1, inplace=True) explode_df_uniprot.drop_duplicates(inplace=True) @@ -324,16 +326,16 @@ def _preprocess_ncbi_data(self) -> pandas.DataFrame: explode_df_ncbi_gene['entrez_gene_type'] = explode_df_ncbi_gene['type_of_gene'] gene_dict = self.genomic_type_mapper['entrez_gene_type'] for val in gene_dict.keys(): - explode_df_ncbi_gene['entrez_gene_type'].replace(val, gene_dict[val], inplace=True) + explode_df_ncbi_gene['entrez_gene_type'].str.replace(val, gene_dict[val], inplace=True) # reformat master gene type explode_df_ncbi_gene['master_gene_type'] = explode_df_ncbi_gene['entrez_gene_type'] gene_dict = self.genomic_type_mapper['master_gene_type'] for val in gene_dict.keys(): - explode_df_ncbi_gene['master_gene_type'].replace(val, gene_dict[val], inplace=True) + explode_df_ncbi_gene['master_gene_type'].str.replace(val, gene_dict[val], inplace=True) # post-process reformatted data - explode_df_ncbi_gene['hgnc_id'] = explode_df_ncbi_gene['hgnc_id'].replace('HGNC:', '', regex=True) - explode_df_ncbi_gene['ensembl_gene_id'] = explode_df_ncbi_gene['ensembl_gene_id'].replace('Ensembl:', '', - regex=True) + explode_df_ncbi_gene['hgnc_id'] = explode_df_ncbi_gene['hgnc_id'].str.replace('HGNC:', '', regex=True) + explode_df_ncbi_gene['ensembl_gene_id'] = explode_df_ncbi_gene['ensembl_gene_id'].str.replace('Ensembl:', '', + regex=True) explode_df_ncbi_gene.drop(['type_of_gene', 'dbXrefs', 'description', 'Nomenclature_status', 'Modification_date', 'LocusTag', '#tax_id', 'Full_name_from_nomenclature_authority', 'Feature_type', 'Symbol_from_nomenclature_authority'], axis=1, inplace=True) @@ -355,8 +357,8 @@ def _preprocess_protein_ontology_mapping_data(self) -> pandas.DataFrame: pro = self.reads_gcs_bucket_data_to_df(f_name='promapping.txt', delm='\t', head=col_names) pro = pro.loc[pro['Entry'].apply(lambda x: x.startswith('UniProtKB:') and '_VAR' not in x and ', ' not in x)] pro = pro.loc[pro['pro_mapping'].apply(lambda x: x.startswith('exact'))] - pro['pro_id'].replace('PR:', 'PR_', inplace=True, regex=True) # replace PR: with PR_ - pro['Entry'].replace('(^\w*\:)', '', inplace=True, regex=True) # remove ids which appear before ':' + pro['pro_id'].str.replace('PR:', 'PR_', inplace=True, regex=True) # replace PR: with PR_ + pro['Entry'].str.replace('(^\w*\:)', '', inplace=True, regex=True) # remove ids which appear before ':' pro = pro.loc[pro['pro_id'].apply(lambda x: '-' not in x)] # remove isoforms pro.rename(columns={'Entry': 'uniprot_id'}, inplace=True) pro.drop(['pro_mapping'], axis=1, inplace=True); pro.drop_duplicates(subset=None, keep='first', inplace=True) @@ -412,12 +414,12 @@ def _fixes_genomic_symbols(self) -> pandas.DataFrame: else: clean_dates.append(x) merged_data['symbol'] = clean_dates; merged_data.fillna('None', inplace=True) # make sure that all gene and transcript type columns have none recoded to unknown or not protein-coding - merged_data['hgnc_gene_type'].replace('None', 'unknown', inplace=True, regex=False) - merged_data['ensembl_gene_type'].replace('None', 'unknown', inplace=True, regex=False) - merged_data['entrez_gene_type'].replace('None', 'unknown', inplace=True, regex=False) - merged_data['master_gene_type'].replace('None', 'unknown', inplace=True, regex=False) - merged_data['master_transcript_type'].replace('None', 'not protein-coding', inplace=True, regex=False) - merged_data['ensembl_transcript_type'].replace('None', 'unknown', inplace=True, regex=False) + merged_data['hgnc_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False) + merged_data['ensembl_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False) + merged_data['entrez_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False) + merged_data['master_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False) + merged_data['master_transcript_type'].str.replace('None', 'not protein-coding', inplace=True, regex=False) + merged_data['ensembl_transcript_type'].str.replace('None', 'unknown', inplace=True, regex=False) merged_data_clean = merged_data.drop_duplicates() return merged_data_clean @@ -782,7 +784,7 @@ def processes_hpa_gtex_data(self) -> None: hpa = self._extracts_hpa_tissue_information(); f_name = 'GTEx_Analysis_*_RNASeQC*_gene_median_tpm.gct' gtex = self.reads_gcs_bucket_data_to_df(f_name=f_name, delm='\t', skip=2, head=0) - gtex.fillna('None', inplace=True); gtex['Name'].replace('(\..*)', '', inplace=True, regex=True) + gtex.fillna('None', inplace=True); gtex['Name'].str.replace('(\..*)', '', inplace=True, regex=True) # process human protein atlas data hpa_results = [] for idx, row in tqdm(hpa.iterrows(), total=hpa.shape[0]): @@ -793,7 +795,7 @@ def processes_hpa_gtex_data(self) -> None: if ';' in row_val: for x in row_val.split(';'): x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1]) - hpa_results += [ [ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] + hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] else: x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1]) hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] @@ -841,8 +843,8 @@ def processes_hpa_gtex_data(self) -> None: for idx, row in tqdm(gtex.iterrows(), total=gtex.shape[0]): for col in list(gtex.columns)[2:]: typ = 'cell line' if 'Cells' in col else 'anatomy'; evid = 'Evidence at transcript level' - gtex_results += [ - [str(row['Name']), str(row['Description']), 'None', evid, typ, 'None', col, float(row[col]), source]] + gtex_results += [[str(row['Name']), str(row['Description']), + 'None', evid, typ, 'None', col, float(row[col]), source]] # write results filename = 'HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt' with open(self.temp_dir + '/' + filename, 'w') as out: @@ -1308,8 +1310,8 @@ def processes_clinvar_data(self) -> None: # explode nested data explode_df_clinvar = explodes_data(clinvar_data.copy(), ['PhenotypeIDS'], ';') explode_df_clinvar = explodes_data(explode_df_clinvar.copy(), ['PhenotypeIDS'], ',') - explode_df_clinvar['PhenotypeIDS'].replace('Orphanet:ORPHA', 'ORPHA:', inplace=True, regex=True) - explode_df_clinvar['PhenotypeIDS'].replace('Human Phenotype Ontology:HP:', 'HP_', inplace=True, regex=True) + explode_df_clinvar['PhenotypeIDS'].str.replace('Orphanet:ORPHA', 'ORPHA:', inplace=True, regex=True) + explode_df_clinvar['PhenotypeIDS'].str.replace('Human Phenotype Ontology:HP:', 'HP_', inplace=True, regex=True) filename = 'CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt' explode_df_clinvar.to_csv(self.temp_dir + '/' + filename, sep='\t', encoding='utf-8', index=False) uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, filename) @@ -1477,7 +1479,7 @@ def _creates_variant_metadata_dict(self) -> Dict: "cytogenetic location:{}) and has clinical significance '{}'. " + \ "This entry is for the {} and was last reviewed on {} with review status '{}'." desc.append( - sent.format(row['Origin'].replace(';', '/'), row['Type'].replace(';', '/'), row['Chromosome'], + sent.format(row['Origin'].str.replace(';', '/'), row['Type'].replace(';', '/'), row['Chromosome'], row['ChromosomeAccession'], row['Start'], row['Stop'], row['Cytogenetic'], row['ClinicalSignificance'], row['Assembly'], row['LastEvaluated'], row['ReviewStatus']).replace('None', 'UNKNOWN')) @@ -1546,7 +1548,7 @@ def _creates_pathway_metadata_dict(self) -> Dict: g = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name1, self.temp_dir) data1 = pandas.read_csv(g, header=None, delimiter='\t', skiprows=4, low_memory=False) data1 = data1.loc[data1[12].apply(lambda x: x == 'taxon:9606')] - data1[5].replace('REACTOME:', '', inplace=True, regex=True) + data1[5].str.replace('REACTOME:', '', inplace=True, regex=True) # reactome CHEBI data f_name2 = 'ChEBI2Reactome_All_Levels.txt' h = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name2, self.temp_dir) @@ -1581,7 +1583,7 @@ def _creates_relations_metadata_dict(self) -> Dict: f_name = 'ro_with_imports.owl' x = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name, self.temp_dir) ro_graph = Graph().parse(x) - relation_metadata_dict, obo = {}, Namespace('http://purl.obolibrary.org/obo/') + relation_metadata_dict = {} cls = [x for x in gets_ontology_classes(ro_graph) if '/RO_' in str(x)] + \ [x for x in gets_object_properties(ro_graph) if '/RO_' in str(x)] master_synonyms = [x for x in ro_graph if 'synonym' in str(x[1]).lower() and isinstance(x[0], URIRef)] diff --git a/notebooks/Data_Preparation.ipynb b/notebooks/Data_Preparation.ipynb index 750ee0ba..4da31e74 100644 --- a/notebooks/Data_Preparation.ipynb +++ b/notebooks/Data_Preparation.ipynb @@ -110,18 +110,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ - "# if running a local version of pkt_kg, uncomment the code below\n", + "# # # if running a local version of pkt_kg, uncomment the code below\n", "# import sys\n", "# sys.path.append('../')" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -159,7 +159,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -1016,7 +1016,7 @@ "hgnc = hgnc.loc[hgnc['status'].apply(lambda x: x == 'Approved')]\n", "hgnc = hgnc[['hgnc_id', 'entrez_id', 'ensembl_gene_id', 'uniprot_ids', 'symbol', 'locus_type', 'alias_symbol', 'name', 'location', 'alias_name']]\n", "hgnc.rename(columns={'uniprot_ids': 'uniprot_id', 'location': 'map_location', 'locus_type': 'hgnc_gene_type'}, inplace=True)\n", - "hgnc['hgnc_id'].replace('.*\\:', '', inplace=True, regex=True) # strip 'HGNC' off of the identifiers\n", + "hgnc['hgnc_id'].str.replace('.*\\:', '', inplace=True, regex=True) # strip 'HGNC' off of the identifiers\n", "hgnc.fillna('None', inplace=True) # replace NaN with 'None'\n", "hgnc['entrez_id'] = hgnc['entrez_id'].apply(lambda x: str(int(x)) if x != 'None' else 'None') # make col str\n", "\n", @@ -1030,13 +1030,13 @@ "\n", "# reformat hgnc gene type\n", "for val in genomic_type_mapper['hgnc_gene_type'].keys():\n", - " explode_df_hgnc['hgnc_gene_type'].replace(val, genomic_type_mapper['hgnc_gene_type'][val], inplace=True)\n", + " explode_df_hgnc['hgnc_gene_type'].str.replace(val, genomic_type_mapper['hgnc_gene_type'][val], inplace=True)\n", "\n", "# reformat master hgnc gene type\n", "explode_df_hgnc['master_gene_type'] = explode_df_hgnc['hgnc_gene_type']\n", "master_dict = genomic_type_mapper['hgnc_master_gene_type']\n", "for val in master_dict.keys():\n", - " explode_df_hgnc['master_gene_type'].replace(val, master_dict[val], inplace=True)\n", + " explode_df_hgnc['master_gene_type'].str.replace(val, master_dict[val], inplace=True)\n", "\n", "# post-process reformatted data\n", "explode_df_hgnc.drop(['alias_symbol', 'alias_name'], axis=1, inplace=True) # remove original gene type column\n", @@ -1104,16 +1104,16 @@ "\n", "# reformat ensembl gene type\n", "gene_dict = genomic_type_mapper['ensembl_gene_type']\n", - "for val in gene_dict.keys(): ensembl_geneset['ensembl_gene_type'].replace(val, gene_dict[val], inplace=True)\n", + "for val in gene_dict.keys(): ensembl_geneset['ensembl_gene_type'].str.replace(val, gene_dict[val], inplace=True)\n", "# reformat master gene type\n", "ensembl_geneset['master_gene_type'] = ensembl_geneset['ensembl_gene_type']\n", "gene_dict = genomic_type_mapper['ensembl_master_gene_type']\n", - "for val in gene_dict.keys(): ensembl_geneset['master_gene_type'].replace(val, gene_dict[val], inplace=True)\n", + "for val in gene_dict.keys(): ensembl_geneset['master_gene_type'].str.replace(val, gene_dict[val], inplace=True)\n", "# reformat master transcript type\n", - "ensembl_geneset['ensembl_transcript_type'].replace('vault_RNA', 'vaultRNA', inplace=True, regex=False)\n", + "ensembl_geneset['ensembl_transcript_type'].str.replace('vault_RNA', 'vaultRNA', inplace=True, regex=False)\n", "ensembl_geneset['master_transcript_type'] = ensembl_geneset['ensembl_transcript_type']\n", "trans_dict = genomic_type_mapper['ensembl_master_transcript_type']\n", - "for val in trans_dict.keys(): ensembl_geneset['master_transcript_type'].replace(val, trans_dict[val], inplace=True)\n", + "for val in trans_dict.keys(): ensembl_geneset['master_transcript_type'].str.replace(val, trans_dict[val], inplace=True)\n", "\n", "# post-process reformatted data\n", "ensembl_geneset.drop_duplicates(subset=None, keep='first', inplace=True)\n", @@ -1326,7 +1326,7 @@ "explode_df_uniprot = explodes_data(explode_df_uniprot.copy(), ['symbol', 'synonyms'], '|')\n", "\n", "# strip out uniprot names\n", - "explode_df_uniprot['transcript_stable_id'].replace('\\s.*','', inplace=True, regex=True)\n", + "explode_df_uniprot['transcript_stable_id'].str.replace('\\s.*','', inplace=True, regex=True)\n", "\n", "# remove duplicates\n", "explode_df_uniprot.drop(['Status'], axis=1, inplace=True)\n", @@ -1395,18 +1395,18 @@ "# reformat entrez gene type\n", "explode_df_ncbi_gene['entrez_gene_type'] = explode_df_ncbi_gene['type_of_gene']\n", "gene_dict = genomic_type_mapper['entrez_gene_type']\n", - "for val in gene_dict.keys(): explode_df_ncbi_gene['entrez_gene_type'].replace(val, gene_dict[val], inplace=True)\n", + "for val in gene_dict.keys(): explode_df_ncbi_gene['entrez_gene_type'].str.replace(val, gene_dict[val], inplace=True)\n", "# reformat master gene type\n", "explode_df_ncbi_gene['master_gene_type'] = explode_df_ncbi_gene['entrez_gene_type']\n", "gene_dict = genomic_type_mapper['master_gene_type']\n", - "for val in gene_dict.keys(): explode_df_ncbi_gene['master_gene_type'].replace(val, gene_dict[val], inplace=True)\n", + "for val in gene_dict.keys(): explode_df_ncbi_gene['master_gene_type'].str.replace(val, gene_dict[val], inplace=True)\n", "\n", "# post-process reformatted data\n", "explode_df_ncbi_gene.drop(['type_of_gene', 'dbXrefs', 'description', 'Nomenclature_status', 'Modification_date',\n", " 'LocusTag', '#tax_id', 'Full_name_from_nomenclature_authority', 'Feature_type',\n", " 'Symbol_from_nomenclature_authority'], axis=1, inplace=True)\n", - "explode_df_ncbi_gene['hgnc_id'] = explode_df_ncbi_gene['hgnc_id'].replace('HGNC:', '', regex=True)\n", - "explode_df_ncbi_gene['ensembl_gene_id'] = explode_df_ncbi_gene['ensembl_gene_id'].replace('Ensembl:', '', regex=True)\n", + "explode_df_ncbi_gene['hgnc_id'] = explode_df_ncbi_gene['hgnc_id'].str.replace('HGNC:', '', regex=True)\n", + "explode_df_ncbi_gene['ensembl_gene_id'] = explode_df_ncbi_gene['ensembl_gene_id'].str.replace('Ensembl:', '', regex=True)\n", "explode_df_ncbi_gene.drop_duplicates(subset=None, keep='first', inplace=True)\n", "\n", "# preview data\n", @@ -1453,8 +1453,8 @@ "source": [ "pro_map = pro_map.loc[pro_map['entry'].apply(lambda x: x.startswith('Uni') and '_VAR' not in x and ', ' not in x)] # keep 'UniProtKB' rows\n", "pro_map = pro_map.loc[pro_map['pro_mapping'].apply(lambda x: x.startswith('exact'))] # keep exact mappings\n", - "pro_map['pro_id'].replace('PR:','PR_', inplace=True, regex=True) # replace PR: with PR_\n", - "pro_map['entry'].replace('(^\\w*\\:)','', inplace=True, regex=True) # remove id prefixes\n", + "pro_map['pro_id'].str.replace('PR:','PR_', inplace=True, regex=True) # replace PR: with PR_\n", + "pro_map['entry'].str.replace('(^\\w*\\:)','', inplace=True, regex=True) # remove id prefixes\n", "pro_map = pro_map.loc[pro_map['pro_id'].apply(lambda x: '-' not in x)] # remove isoforms\n", "pro_map.rename(columns={'entry': 'uniprot_id'}, inplace=True) # rename columns before merging\n", "pro_map.drop(['pro_mapping'], axis=1, inplace=True) # remove uneeded columns\n", @@ -1604,12 +1604,12 @@ "merged_data.fillna('None', inplace=True)\n", "\n", "# make sure that all gene and transcript type colunmns have none recoded to unknown or not protein-coding\n", - "merged_data['hgnc_gene_type'].replace('None', 'unknown', inplace=True, regex=False)\n", - "merged_data['ensembl_gene_type'].replace('None', 'unknown', inplace=True, regex=False)\n", - "merged_data['entrez_gene_type'].replace('None', 'unknown', inplace=True, regex=False)\n", - "merged_data['master_gene_type'].replace('None', 'unknown', inplace=True, regex=False)\n", - "merged_data['master_transcript_type'].replace('None', 'not protein-coding', inplace=True, regex=False)\n", - "merged_data['ensembl_transcript_type'].replace('None', 'unknown', inplace=True, regex=False)\n", + "merged_data['hgnc_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", + "merged_data['ensembl_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", + "merged_data['entrez_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", + "merged_data['master_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", + "merged_data['master_transcript_type'].str.replace('None', 'not protein-coding', inplace=True, regex=False)\n", + "merged_data['ensembl_transcript_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", "\n", "# remove duplicates\n", "merged_data_clean = merged_data.drop_duplicates(subset=None, keep='first')\n", @@ -2573,7 +2573,7 @@ "# load data\n", "gtex = pandas.read_csv(unprocessed_data_location + 'GTEx_Analysis_2017-06-05_v8_RNASeQCv1.1.9_gene_median_tpm.gct', header=0, skiprows=2, delimiter='\\t')\n", "gtex.fillna('None', inplace=True) # replace NaN with 'None'\n", - "gtex['Name'].replace('(\\..*)','', inplace=True, regex=True) # remove identifier type, which appears after '.'\n" + "gtex['Name'].str.replace('(\\..*)','', inplace=True, regex=True) # remove identifier type, which appears after '.'\n" ] }, { @@ -3219,9 +3219,9 @@ "transcripts = {}\n", "for idx, row in tqdm(transcript_data.iterrows(), total=transcript_data.shape[0]):\n", " if row['transcript_stable_id'] != 'None':\n", - " if row['transcript_stable_id'].replace('transcript_stable_id_', '') in transcripts.keys():\n", - " transcripts[row['transcript_stable_id'].replace('transcript_stable_id_', '')] += [row['ensembl_transcript_type']]\n", - " else: transcripts[row['transcript_stable_id'].replace('transcript_stable_id_', '')] = [row['ensembl_transcript_type']]\n", + " if row['transcript_stable_id'].str.replace('transcript_stable_id_', '') in transcripts.keys():\n", + " transcripts[row['transcript_stable_id'].str.replace('transcript_stable_id_', '')] += [row['ensembl_transcript_type']]\n", + " else: transcripts[row['transcript_stable_id'].str.replace('transcript_stable_id_', '')] = [row['ensembl_transcript_type']]\n", " \n", "# update so map dictionary\n", "for identifier in tqdm(transcripts.keys()):\n", @@ -3731,34 +3731,1301 @@ "\n", "**Data Source Wiki Page:** [Clinvar](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#clinvar) \n", "\n", - "**Purpose:** This script downloads the [variant_summary.txt](ftp://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz) file from [ClinVar](https://www.ncbi.nlm.nih.gov/clinvar/) in order to create the following edges: \n", + "**Purpose:** This script downloads the data files list below in order to create the following edges: \n", "- gene-variant \n", "- variant-disease \n", "- variant-phenotype \n", "\n", + "**Data Files:** \n", + "Details on each file have been taken from this [README](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/README.txt) and are provided in relevant code chunks below. \n", + "##### *Core Data Files* <a class=\"anchor\" id=\"core-data-files\"></a> \n", + "- [`variant_summary.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz) \n", + "- [`submission_summary.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz) \n", + "- [`disease_names`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names)\n", + "\n", + "##### *Metadata Files*<a class=\"anchor\" id=\"metadata-files\"></a> \n", + "- [`var_citations.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt) \n", + "- [`allele_gene.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz) \n", + "- [`gene_specific_summary.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt) \n", + "- [`gene_condition_source_id`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id) \n", + "\n", "**Output:** `CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt`\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "#### Download and Process Core Data Files <a class=\"anchor\" id=\"core-data-files\"></a>\n", + "***\n", + "\n", + "*Data Files:* \n", + "- [`variant_summary.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz) \n", + "- [`submission_summary.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz) \n", + "- [`disease_names`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names)\n", + "\n", + "*Processing Details* \n", + "<u>Step 1</u>: The first step is down the `variant_summary.txt.gz`, `submission_summary.txt.gz`, and `disease_names` files. After downloading, the files are cleaned to handle missing data, unneeded variables are removed, identifiers and date fields are cleaned and reformatted, and rows without disease/phenotype identifiers are removed (i.e., [`MedGen:CN517202`](https://www.ncbi.nlm.nih.gov/medgen/CN517202)). \n", + "\n", + "<u>Step 2</u>: Merge the `submission_summary`, and `variant_summary` files, back-fill missing information that could not be recovered in the merge, and process and unify submitted and reported disease identifiers.\n", + "\n", + "<u>Step 3</u>: Merge the `submission_summary`, and `disease_names` files to try and recover phenotype entries that were initially submitted as a string, but have no identifier." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "[**`variant_summary.txt.gz`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz)\n", + "\n", + "> A tab-delimited report based on each variant at a location on the genome for which data have been submitted to ClinVar. \n", + "The data for the variant are reported for each assembly, so most variants have a line for GRCh37 (hg19) and another line for GRCh38 (hg38).\n", + ">\n", + "> - <u>AlleleID</u>: integer value as stored in the AlleleID field in ClinVar \n", + "> - <u>Type</u>: character, the type of variant represented by the AlleleID \n", + "> - <u>Name</u>: character, ClinVar's preferred name for the record with this AlleleID \n", + "> - <u>GeneID</u>: integer, GeneID in NCBI's Gene database, reported if there is a single gene, otherwise reported as -1. \n", + "> - <u>GeneSymbol</u>: character, comma-separated list of GeneIDs overlapping the variant \n", + "> - <u>HGNC_ID</u>: string, of format HGNC:integer, reported if there is a single GeneID. \n", + "> - <u>ClinicalSignificance</u>: character, comma-separated list of aggregate values of clinical significance calculated for this variant. \n", + "> - <u>ClinSigSimple</u>: integer, \n", + " 0 = no current value of Likely pathogenic or Pathogenic;\n", + " 1 = at least one current record submitted with an interpretation of Likely pathogenic or \n", + " Pathogenic (independent of whether that record includes assertion criteria and \n", + " evidence) \n", + " -1 = no values for clinical significance at all for this variant or set of variants; \n", + " used for the \"included\" variants that are only in ClinVar because they are included\n", + " in a haplotype or genotype with an interpretation \n", + "> - <u>LastEvaluated</u>: date, the latest date any submitter reported clinical significance \n", + "> - <u>RS# (dbSNP)</u>: integer, rs# in dbSNP, reported as -1 if missing \n", + "> - <u>nsv/esv (dbVar)</u>: character, the NSV identifier for the region in dbVar \n", + "> - <u>RCVaccession</u>: character, list of RCV accessions that report this variant \n", + "> - <u>PhenotypeIDs</u>: character, list of identifiers for phenotype(s) interpreted for this variant. If more than 5 conditions are reported, the number of conditions is reported instead. \n", + "> - <u>PhenotypeList</u>: character, list of names corresponding to PhenotypeIDs. If more than 5 conditions are reported, the number of conditions is reported instead. \n", + "> - <u>Origin</u>: character, list of all allelic origins for this variant \n", + "> - <u>OriginSimple</u>: character, processed from Origin to make it easier to distinguish between germline and somatic \n", + "> - <u>Assembly</u>: character, name of the assembly on which locations are based \n", + "> - <u>ChromosomeAccession</u>: Accession and version of the RefSeq sequence defining the position reported in the start and stop columns. \n", + "> - <u>Chromosome</u>: character, chromosomal location \n", + "> - <u>Start</u>: integer, starting location, right-shifted, in pter->qter orientation \n", + "> - <u>Stop</u>: integer, end location, right-shifted, in pter->qter orientation \n", + "> - <u>ReferenceAllele</u>: The reference allele using the right-shifted location in Start and Stop. \n", + "> - <u>AlternateAllele</u>: The alternate allele using the right-shifted location in Start and Stop. \n", + "> - <u>Cytogenetic</u>: character, ISCN band\n", + "> - <u>ReviewStatus</u>: character, highest review status for reporting this measure. \n", + "> - <u>NumberSubmitters</u>: integer, number of submitters describing this variant \n", + "> - <u>Guidelines</u>: character, ACMG only right now \n", + "> - <u>TestedInGTR</u>: character, Y/N for Yes/No if there is a test registered as specific to this variant in the NIH Genetic Testing Registry (GTR) \n", + "> - <u>OtherIDs</u>: character, list of other identifiers or sources of information about this variant \n", + "> - <u>SubmitterCategories</u>: coded value to indicate whether data were submitted by another resource (1), any other type of source (2), both (3), or none (4) \n", + "> - <u>VariationID</u>: The identifier ClinVar uses specific to the AlleleID. Not all VariationIDS that may be related to the AlleleID are reported in this file. \n", + "> - <u>PositionVCF</u>: integer, starting location, left-shifted, in pter->qter orientation \n", + "> - <u>ReferenceAlleleVCF</u>: The reference allele using the left-shifted location in vcf_pos. \n", + "> - <u>AlternateAlleleVCF</u>: The alternate allele using the left-shifted location in vcf_pos. " + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "# download data\n", - "url = 'ftp://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz'\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz'\n", "if not os.path.exists(unprocessed_data_location + 'variant_summary.txt'):\n", " data_downloader(url, unprocessed_data_location)\n", "\n", "# load data\n", - "clinvar_data = pandas.read_csv(unprocessed_data_location + 'variant_summary.txt', header=0, delimiter='\\t', low_memory=False)" + "var_summary = pandas.read_csv(unprocessed_data_location + 'variant_summary.txt',\n", + " header=0, delimiter='\\t', low_memory=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 1259766 variant edges\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>AlleleID</th>\n", + " <th>Type</th>\n", + " <th>Name</th>\n", + " <th>GeneID</th>\n", + " <th>GeneSymbol</th>\n", + " <th>HGNC_ID</th>\n", + " <th>ClinicalSignificance</th>\n", + " <th>ClinSigSimple</th>\n", + " <th>LastEvaluated</th>\n", + " <th>RS# (dbSNP)</th>\n", + " <th>...</th>\n", + " <th>Cytogenetic</th>\n", + " <th>ReviewStatus</th>\n", + " <th>NumberSubmitters</th>\n", + " <th>TestedInGTR</th>\n", + " <th>OtherIDs</th>\n", + " <th>SubmitterCategories</th>\n", + " <th>VariationID</th>\n", + " <th>PositionVCF</th>\n", + " <th>ReferenceAlleleVCF</th>\n", + " <th>AlternateAlleleVCF</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>15041</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>None</td>\n", + " <td>397704705</td>\n", + " <td>...</td>\n", + " <td>7p22.1</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>2</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215070,OMIM:613653.0001</td>\n", + " <td>3</td>\n", + " <td>2</td>\n", + " <td>4820844</td>\n", + " <td>GGAT</td>\n", + " <td>TGCTGTAAACTGTAACTGTAAA</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>15041</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>None</td>\n", + " <td>397704705</td>\n", + " <td>...</td>\n", + " <td>7p22.1</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>2</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215070,OMIM:613653.0001</td>\n", + " <td>3</td>\n", + " <td>2</td>\n", + " <td>4781213</td>\n", + " <td>GGAT</td>\n", + " <td>TGCTGTAAACTGTAACTGTAAA</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>15042</td>\n", + " <td>Deletion</td>\n", + " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>June 29, 2010</td>\n", + " <td>397704709</td>\n", + " <td>...</td>\n", + " <td>7p22.1</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215072,OMIM:613653.0002</td>\n", + " <td>1</td>\n", + " <td>3</td>\n", + " <td>4827360</td>\n", + " <td>GCTGCTGGACCTGCC</td>\n", + " <td>G</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>15042</td>\n", + " <td>Deletion</td>\n", + " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>June 29, 2010</td>\n", + " <td>397704709</td>\n", + " <td>...</td>\n", + " <td>7p22.1</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215072,OMIM:613653.0002</td>\n", + " <td>1</td>\n", + " <td>3</td>\n", + " <td>4787729</td>\n", + " <td>GCTGCTGGACCTGCC</td>\n", + " <td>G</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>15043</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", + " <td>9640</td>\n", + " <td>ZNF592</td>\n", + " <td>HGNC:28986</td>\n", + " <td>Uncertain significance</td>\n", + " <td>0</td>\n", + " <td>June 29, 2015</td>\n", + " <td>150829393</td>\n", + " <td>...</td>\n", + " <td>15q25.3</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O...</td>\n", + " <td>1</td>\n", + " <td>4</td>\n", + " <td>85342440</td>\n", + " <td>G</td>\n", + " <td>A</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>5 rows × 30 columns</p>\n", + "</div>" + ], + "text/plain": [ + " AlleleID Type \\\n", + "0 15041 Indel \n", + "1 15041 Indel \n", + "2 15042 Deletion \n", + "3 15042 Deletion \n", + "4 15043 single nucleotide variant \n", + "\n", + " Name GeneID GeneSymbol \\\n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", + "1 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", + "2 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", + "3 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", + "4 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", + "\n", + " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", + "0 HGNC:22197 Pathogenic 1 None \n", + "1 HGNC:22197 Pathogenic 1 None \n", + "2 HGNC:22197 Pathogenic 1 June 29, 2010 \n", + "3 HGNC:22197 Pathogenic 1 June 29, 2010 \n", + "4 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", + "\n", + " RS# (dbSNP) ... Cytogenetic ReviewStatus \\\n", + "0 397704705 ... 7p22.1 criteria provided, single submitter \n", + "1 397704705 ... 7p22.1 criteria provided, single submitter \n", + "2 397704709 ... 7p22.1 no assertion criteria provided \n", + "3 397704709 ... 7p22.1 no assertion criteria provided \n", + "4 150829393 ... 15q25.3 no assertion criteria provided \n", + "\n", + " NumberSubmitters TestedInGTR \\\n", + "0 2 N \n", + "1 2 N \n", + "2 1 N \n", + "3 1 N \n", + "4 1 N \n", + "\n", + " OtherIDs SubmitterCategories \\\n", + "0 ClinGen:CA215070,OMIM:613653.0001 3 \n", + "1 ClinGen:CA215070,OMIM:613653.0001 3 \n", + "2 ClinGen:CA215072,OMIM:613653.0002 1 \n", + "3 ClinGen:CA215072,OMIM:613653.0002 1 \n", + "4 ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O... 1 \n", + "\n", + " VariationID PositionVCF ReferenceAlleleVCF AlternateAlleleVCF \n", + "0 2 4820844 GGAT TGCTGTAAACTGTAACTGTAAA \n", + "1 2 4781213 GGAT TGCTGTAAACTGTAACTGTAAA \n", + "2 3 4827360 GCTGCTGGACCTGCC G \n", + "3 3 4787729 GCTGCTGGACCTGCC G \n", + "4 4 85342440 G A \n", + "\n", + "[5 rows x 30 columns]" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# replace NaN, and \"-\" with 'None'\n", + "var_summary.fillna('None', inplace=True)\n", + "var_summary = var_summary.replace('na', 'None')\n", + "var_summary = var_summary.replace('-', 'None')\n", + "\n", + "# handle ids that are coded as missing (i.e., -1)\n", + "var_summary = var_summary[var_summary['GeneID'] != -1]\n", + "var_summary = var_summary[var_summary['RS# (dbSNP)'] != -1]\n", + "\n", + "# remove rows without an assembly\n", + "var_summary = var_summary[var_summary['Assembly'] != 'None']\n", + "\n", + "# replace cells that contain \";unknown\" with ''\n", + "var_summary = var_summary.replace(';unknown', '')\n", + "\n", + "# convert date format\n", + "var_summary['LastEvaluated'] = var_summary['LastEvaluated'].str.replace('None', '')\n", + "var_summary['LastEvaluated'] = pandas.to_datetime(var_summary['LastEvaluated'])\n", + "var_summary['LastEvaluated'] = var_summary['LastEvaluated'].dt.strftime('%B %d, %Y')\n", + "var_summary['LastEvaluated'].fillna('None', inplace=True)\n", + "\n", + "# rename variables\n", + "var_summary.rename(columns={'#AlleleID': 'AlleleID'}, inplace=True)\n", + "\n", + "# remove unneeded variables\n", + "drop_list = ['nsv/esv (dbVar)', 'RCVaccession', 'OriginSimple', 'Guidelines']\n", + "var_summary = var_summary.drop(drop_list, axis = 1).drop_duplicates()\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} variant edges'.format(edge_count=len(var_summary)))\n", + "var_summary.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Address Duplicate Rows for GRCh37 and GRCh38 Assemblies*" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 2/2 [07:04<00:00, 212.30s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 629684 edges\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>AlleleID</th>\n", + " <th>Type</th>\n", + " <th>Name</th>\n", + " <th>GeneID</th>\n", + " <th>GeneSymbol</th>\n", + " <th>HGNC_ID</th>\n", + " <th>ClinicalSignificance</th>\n", + " <th>ClinSigSimple</th>\n", + " <th>LastEvaluated</th>\n", + " <th>RS# (dbSNP)</th>\n", + " <th>...</th>\n", + " <th>PhenotypeList</th>\n", + " <th>Origin</th>\n", + " <th>ReviewStatus</th>\n", + " <th>NumberSubmitters</th>\n", + " <th>TestedInGTR</th>\n", + " <th>OtherIDs</th>\n", + " <th>SubmitterCategories</th>\n", + " <th>VariationID</th>\n", + " <th>GRCh37_Assembly</th>\n", + " <th>GRCh38_Assembly</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>15041</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>None</td>\n", + " <td>397704705</td>\n", + " <td>...</td>\n", + " <td>Spastic paraplegia 48, autosomal recessive</td>\n", + " <td>germline;unknown</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>2</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215070,OMIM:613653.0001</td>\n", + " <td>3</td>\n", + " <td>2</td>\n", + " <td>{'ChromosomeAccession': 'NC_000007.13', 'Chrom...</td>\n", + " <td>{'ChromosomeAccession': 'NC_000007.14', 'Chrom...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>15042</td>\n", + " <td>Deletion</td>\n", + " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>June 29, 2010</td>\n", + " <td>397704709</td>\n", + " <td>...</td>\n", + " <td>Spastic paraplegia 48, autosomal recessive</td>\n", + " <td>germline</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215072,OMIM:613653.0002</td>\n", + " <td>1</td>\n", + " <td>3</td>\n", + " <td>{'ChromosomeAccession': 'NC_000007.13', 'Chrom...</td>\n", + " <td>{'ChromosomeAccession': 'NC_000007.14', 'Chrom...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>15043</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", + " <td>9640</td>\n", + " <td>ZNF592</td>\n", + " <td>HGNC:28986</td>\n", + " <td>Uncertain significance</td>\n", + " <td>0</td>\n", + " <td>June 29, 2015</td>\n", + " <td>150829393</td>\n", + " <td>...</td>\n", + " <td>Galloway-Mowat syndrome 1</td>\n", + " <td>germline</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O...</td>\n", + " <td>1</td>\n", + " <td>4</td>\n", + " <td>{'ChromosomeAccession': 'NC_000015.9', 'Chromo...</td>\n", + " <td>{'ChromosomeAccession': 'NC_000015.10', 'Chrom...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>15044</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", + " <td>55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>HGNC:26927</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>December 30, 2019</td>\n", + " <td>267606829</td>\n", + " <td>...</td>\n", + " <td>not provided|Leigh syndrome|Mitochondrial comp...</td>\n", + " <td>germline</td>\n", + " <td>criteria provided, multiple submitters, no con...</td>\n", + " <td>3</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA113792,OMIM:613622.0001</td>\n", + " <td>3</td>\n", + " <td>5</td>\n", + " <td>{'ChromosomeAccession': 'NC_000011.9', 'Chromo...</td>\n", + " <td>{'ChromosomeAccession': 'NC_000011.10', 'Chrom...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>15045</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", + " <td>55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>HGNC:26927</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>October 01, 2010</td>\n", + " <td>267606830</td>\n", + " <td>...</td>\n", + " <td>Mitochondrial complex 1 deficiency, nuclear ty...</td>\n", + " <td>germline</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>N</td>\n", + " <td>UniProtKB:Q96CU9#VAR_064571,OMIM:613622.0002,C...</td>\n", + " <td>1</td>\n", + " <td>6</td>\n", + " <td>{'ChromosomeAccession': 'NC_000011.9', 'Chromo...</td>\n", + " <td>{'ChromosomeAccession': 'NC_000011.10', 'Chrom...</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>5 rows × 21 columns</p>\n", + "</div>" + ], + "text/plain": [ + " AlleleID Type \\\n", + "0 15041 Indel \n", + "1 15042 Deletion \n", + "2 15043 single nucleotide variant \n", + "3 15044 single nucleotide variant \n", + "4 15045 single nucleotide variant \n", + "\n", + " Name GeneID GeneSymbol \\\n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", + "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", + "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", + "3 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", + "4 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", + "\n", + " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", + "0 HGNC:22197 Pathogenic 1 None \n", + "1 HGNC:22197 Pathogenic 1 June 29, 2010 \n", + "2 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", + "3 HGNC:26927 Pathogenic 1 December 30, 2019 \n", + "4 HGNC:26927 Pathogenic 1 October 01, 2010 \n", + "\n", + " RS# (dbSNP) ... PhenotypeList \\\n", + "0 397704705 ... Spastic paraplegia 48, autosomal recessive \n", + "1 397704709 ... Spastic paraplegia 48, autosomal recessive \n", + "2 150829393 ... Galloway-Mowat syndrome 1 \n", + "3 267606829 ... not provided|Leigh syndrome|Mitochondrial comp... \n", + "4 267606830 ... Mitochondrial complex 1 deficiency, nuclear ty... \n", + "\n", + " Origin ReviewStatus \\\n", + "0 germline;unknown criteria provided, single submitter \n", + "1 germline no assertion criteria provided \n", + "2 germline no assertion criteria provided \n", + "3 germline criteria provided, multiple submitters, no con... \n", + "4 germline no assertion criteria provided \n", + "\n", + " NumberSubmitters TestedInGTR \\\n", + "0 2 N \n", + "1 1 N \n", + "2 1 N \n", + "3 3 N \n", + "4 1 N \n", + "\n", + " OtherIDs SubmitterCategories \\\n", + "0 ClinGen:CA215070,OMIM:613653.0001 3 \n", + "1 ClinGen:CA215072,OMIM:613653.0002 1 \n", + "2 ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O... 1 \n", + "3 ClinGen:CA113792,OMIM:613622.0001 3 \n", + "4 UniProtKB:Q96CU9#VAR_064571,OMIM:613622.0002,C... 1 \n", + "\n", + " VariationID GRCh37_Assembly \\\n", + "0 2 {'ChromosomeAccession': 'NC_000007.13', 'Chrom... \n", + "1 3 {'ChromosomeAccession': 'NC_000007.13', 'Chrom... \n", + "2 4 {'ChromosomeAccession': 'NC_000015.9', 'Chromo... \n", + "3 5 {'ChromosomeAccession': 'NC_000011.9', 'Chromo... \n", + "4 6 {'ChromosomeAccession': 'NC_000011.9', 'Chromo... \n", + "\n", + " GRCh38_Assembly \n", + "0 {'ChromosomeAccession': 'NC_000007.14', 'Chrom... \n", + "1 {'ChromosomeAccession': 'NC_000007.14', 'Chrom... \n", + "2 {'ChromosomeAccession': 'NC_000015.10', 'Chrom... \n", + "3 {'ChromosomeAccession': 'NC_000011.10', 'Chrom... \n", + "4 {'ChromosomeAccession': 'NC_000011.10', 'Chrom... \n", + "\n", + "[5 rows x 21 columns]" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# identify columns to process\n", + "assemb_cols = ['ChromosomeAccession', 'Chromosome', 'Start', 'Stop', 'ReferenceAllele',\n", + " 'AlternateAllele','Cytogenetic', 'PositionVCF', 'ReferenceAlleleVCF', 'AlternateAlleleVCF']\n", + "\n", + "# process each assembly\n", + "assemblies = set(var_summary['Assembly'])\n", + "for assembly in tqdm(assemblies):\n", + " var_summary[assembly + '_Assembly'] = var_summary.apply(\n", + " lambda x: str({col: x[col] for col in assemb_cols if x[col] != 'None'})\n", + " if x['Assembly'] == assembly else numpy.nan, axis=1)\n", + "\n", + "# drop unneeded columns\n", + "var_summary_update = var_summary.copy()\n", + "var_summary_update = var_summary_update.drop(assemb_cols + ['Assembly'], axis = 1)\n", + "\n", + "# unite columns\n", + "group_cols = [x for x in var_summary_update.columns if not x.endswith('_Assembly')]\n", + "temp1 = var_summary_update[group_cols + ['GRCh37_Assembly']].dropna(subset=['GRCh37_Assembly'])\n", + "temp2 = var_summary_update[group_cols + ['GRCh38_Assembly']].dropna(subset=['GRCh38_Assembly'])\n", + "var_summary_update = temp1.merge(temp2, on=group_cols, how='inner')\n", + "\n", + "# sort by VariationID and date and keep only the most recent date for each id\n", + "var_summary_update = var_summary_update.sort_values(['VariationID', 'LastEvaluated'], ascending=[True, False])\n", + "var_summary_update = var_summary_update.drop_duplicates(['VariationID'], keep='last')\n", + "\n", + "# replace NaN, and \"-\" with 'None'\n", + "var_summary_update.fillna('None', inplace=True)\n", + "\n", + "# drop duplicates\n", + "var_summary_update.drop_duplicates(inplace=True)\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(var_summary_update)))\n", + "var_summary_update.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "[**`submission_summary.txt.gz`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz)\n", + "\n", + "> Overview of interpretation, phenotypes, observations, and methods reported in each current submission \n", + ">\n", + "> - <u>VariationID</u>: the identifier assigned by ClinVar \n", + "> - <u>ClinicalSignificance</u>: interpretation of the variation-condition relationship \n", + "> - <u>DateLastEvaluated</u>: the last date the variation-condition relationship was evaluated by this submitter \n", + "> - <u>Description</u>: an optional free text description of the basis of the interpretation \n", + "> - <u>SubmittedPhenotypeInfo</u>: the name(s) or identifier(s) submitted for the condition that was interpreted relative to the variant \n", + "> - <u>ReportedPhenotypeInfo</u>: the MedGen identifier/name combinations ClinVar uses to report the condition that was interpreted. 'na' means there is no public identifier in MedGen for the condition. \n", + "> - <u>ReviewStatus</u>: the level of review for this submission \n", + "> - <u>CollectionMethod</u>: the method by which the submitter obtained the information provided \n", + "> - <u>OriginCounts</u>: the reported origin and the number of observations for each origin \n", + "> - <u>Submitter</u>: the submitter of this record \n", + "> - <u>SCV</u>: the accession and current version assigned by ClinVar to the submitted interpretation of the variation-condition relationship \n", + "> - <u>SubmittedGeneSymbol</u>: the symbol provided by the submitter for the gene affected by the variant. May be null. " + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'submission_summary.txt'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "\n", + "# load data\n", + "submission_summary = pandas.read_csv(unprocessed_data_location + 'submission_summary.txt',\n", + " header=0, skiprows=15, delimiter='\\t', low_memory=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 1120864 edges\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>VariationID</th>\n", + " <th>ClinicalSignificance</th>\n", + " <th>LastEvaluated</th>\n", + " <th>Description</th>\n", + " <th>SubmittedPhenotypeInfo</th>\n", + " <th>ReportedPhenotypeInfo</th>\n", + " <th>ReviewStatus</th>\n", + " <th>CollectionMethod</th>\n", + " <th>OriginCounts</th>\n", + " <th>Submitter</th>\n", + " <th>GeneSymbol</th>\n", + " <th>ExplanationOfInterpretation</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>2</td>\n", + " <td>Pathogenic</td>\n", + " <td>June 29, 2010</td>\n", + " <td>None</td>\n", + " <td>SPASTIC PARAPLEGIA 48, AUTOSOMAL RECESSIVE</td>\n", + " <td>C3150901:Spastic paraplegia 48, autosomal rece...</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>literature only</td>\n", + " <td>germline:na</td>\n", + " <td>OMIM</td>\n", + " <td>AP5Z1</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>3</td>\n", + " <td>Pathogenic</td>\n", + " <td>June 29, 2010</td>\n", + " <td>None</td>\n", + " <td>SPASTIC PARAPLEGIA 48</td>\n", + " <td>C3150901:Spastic paraplegia 48, autosomal rece...</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>literature only</td>\n", + " <td>germline:na</td>\n", + " <td>OMIM</td>\n", + " <td>AP5Z1</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>4</td>\n", + " <td>Uncertain significance</td>\n", + " <td>June 29, 2015</td>\n", + " <td>None</td>\n", + " <td>RECLASSIFIED - VARIANT OF UNKNOWN SIGNIFICANCE</td>\n", + " <td>C4551772:Galloway-Mowat syndrome 1</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>literature only</td>\n", + " <td>germline:na</td>\n", + " <td>OMIM</td>\n", + " <td>ZNF592</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>5</th>\n", + " <td>5</td>\n", + " <td>Pathogenic</td>\n", + " <td>October 01, 2010</td>\n", + " <td>None</td>\n", + " <td>MITOCHONDRIAL COMPLEX I DEFICIENCY, NUCLEAR TY...</td>\n", + " <td>C4748791:Mitochondrial complex 1 deficiency, n...</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>literature only</td>\n", + " <td>germline:na</td>\n", + " <td>OMIM</td>\n", + " <td>FOXRED1</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>5</td>\n", + " <td>Pathogenic</td>\n", + " <td>December 07, 2017</td>\n", + " <td>The Q232X variant in the FOXRED1 gene has been...</td>\n", + " <td>Not Provided</td>\n", + " <td>CN517202:not provided</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>clinical testing</td>\n", + " <td>germline:na</td>\n", + " <td>GeneDx</td>\n", + " <td>FOXRED1</td>\n", + " <td>None</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " VariationID ClinicalSignificance LastEvaluated \\\n", + "0 2 Pathogenic June 29, 2010 \n", + "2 3 Pathogenic June 29, 2010 \n", + "3 4 Uncertain significance June 29, 2015 \n", + "5 5 Pathogenic October 01, 2010 \n", + "4 5 Pathogenic December 07, 2017 \n", + "\n", + " Description \\\n", + "0 None \n", + "2 None \n", + "3 None \n", + "5 None \n", + "4 The Q232X variant in the FOXRED1 gene has been... \n", + "\n", + " SubmittedPhenotypeInfo \\\n", + "0 SPASTIC PARAPLEGIA 48, AUTOSOMAL RECESSIVE \n", + "2 SPASTIC PARAPLEGIA 48 \n", + "3 RECLASSIFIED - VARIANT OF UNKNOWN SIGNIFICANCE \n", + "5 MITOCHONDRIAL COMPLEX I DEFICIENCY, NUCLEAR TY... \n", + "4 Not Provided \n", + "\n", + " ReportedPhenotypeInfo \\\n", + "0 C3150901:Spastic paraplegia 48, autosomal rece... \n", + "2 C3150901:Spastic paraplegia 48, autosomal rece... \n", + "3 C4551772:Galloway-Mowat syndrome 1 \n", + "5 C4748791:Mitochondrial complex 1 deficiency, n... \n", + "4 CN517202:not provided \n", + "\n", + " ReviewStatus CollectionMethod OriginCounts \\\n", + "0 no assertion criteria provided literature only germline:na \n", + "2 no assertion criteria provided literature only germline:na \n", + "3 no assertion criteria provided literature only germline:na \n", + "5 no assertion criteria provided literature only germline:na \n", + "4 criteria provided, single submitter clinical testing germline:na \n", + "\n", + " Submitter GeneSymbol ExplanationOfInterpretation \n", + "0 OMIM AP5Z1 None \n", + "2 OMIM AP5Z1 None \n", + "3 OMIM ZNF592 None \n", + "5 OMIM FOXRED1 None \n", + "4 GeneDx FOXRED1 None " + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# replace NaN and \"-\" with 'None'\n", + "submission_summary.fillna('None', inplace=True)\n", + "submission_summary = submission_summary.replace('na', 'None')\n", + "submission_summary = submission_summary.replace('-', 'None')\n", + "\n", + "# remove rows without an assembly and without a disease annotation\n", + "submission_summary = submission_summary[submission_summary['SubmittedGeneSymbol'] != 'None']\n", + "\n", + "# convert date format\n", + "submission_summary['DateLastEvaluated'] = submission_summary['DateLastEvaluated'].str.replace('None', '')\n", + "submission_summary['DateLastEvaluated'] = pandas.to_datetime(submission_summary['DateLastEvaluated'])\n", + "submission_summary['DateLastEvaluated'] = submission_summary['DateLastEvaluated'].dt.strftime('%B %d, %Y')\n", + "submission_summary['DateLastEvaluated'].fillna('None', inplace=True)\n", + "\n", + "# sort by VariationID and date and keep only the most recent date for each id\n", + "submission_summary = submission_summary.sort_values(['#VariationID', 'DateLastEvaluated'], ascending=[True, False])\n", + "submission_summary = submission_summary.drop_duplicates(['CollectionMethod', '#VariationID'], keep='last')\n", + "\n", + "# rename variables\n", + "submission_summary.rename(columns={'#VariationID': 'VariationID',\n", + " 'DateLastEvaluated': 'LastEvaluated',\n", + " 'SubmittedGeneSymbol': 'GeneSymbol'}, inplace=True)\n", + "\n", + "# remove unneeded variables\n", + "drop_list = ['SCV']\n", + "submission_summary = submission_summary.drop(drop_list, axis = 1).drop_duplicates()\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(submission_summary)))\n", + "submission_summary.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Merge `variant_summary` and `submission_summary` data*\n", + "\n", + "Merge the data on `VariationID`, `GeneSymbol`, `LastEvaluated`, `ReviewStatus`, and `ClinicalSignificance`. Then, back-fill missing information by `VariationID` to recover data that was only available in the `variant_summary` file (i.e., `AlleleID`, `RS# (dbSNP)`, `Type`, `Name`, `GeneID`, `HGNC_ID`, `Origin`) or `submission_summary` file (i.e., `OriginCounts`)." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "# merge files together\n", + "merge_cols = list(set(submission_summary.columns).intersection(set(var_summary_update.columns)))\n", + "var_merged = var_summary_update.merge(submission_summary, on=merge_cols, how='outer')\n", + "\n", + "# reorder columns\n", + "column_order = ['VariationID', 'AlleleID', 'RS# (dbSNP)', 'Type', 'Name',\n", + " 'GeneID', 'HGNC_ID', 'GeneSymbol', 'LastEvaluated',\n", + " 'ReviewStatus', 'Submitter', 'SubmitterCategories',\n", + " 'NumberSubmitters', 'CollectionMethod', 'ClinicalSignificance', \n", + " 'ClinSigSimple','Description', 'SubmittedPhenotypeInfo',\n", + " 'ReportedPhenotypeInfo', 'PhenotypeIDS', 'PhenotypeList', 'OtherIDs',\n", + " 'Origin', 'OriginCounts', 'GRCh37_Assembly', 'GRCh38_Assembly', 'TestedInGTR',\n", + " 'ExplanationOfInterpretation']\n", + "var_merged = var_merged.reindex(columns=column_order)\n", + "\n", + "# sort by VariationID and date and keep only the most recent date for each id\n", + "var_merged = var_merged.sort_values(['VariationID', 'LastEvaluated'], ascending=[True, False]).reset_index(drop=True)\n", + "\n", + "# backfill rows with missing data\n", + "var_merged = var_merged.replace('None', numpy.nan)\n", + "cols = ['AlleleID', 'RS# (dbSNP)', 'Type', 'Name', 'GeneID', 'HGNC_ID', 'Origin', 'OriginCounts']\n", + "var_merged[cols] = var_merged.groupby('VariationID')[cols].ffill().bfill()\n", + "\n", + "# type variables\n", + "var_merged['RS# (dbSNP)'] = pandas.to_numeric(var_merged['RS# (dbSNP)'], downcast='integer', errors='coerce')\n", + "var_merged['AlleleID'] = pandas.to_numeric(var_merged['AlleleID'], downcast='integer', errors='coerce')\n", + "var_merged['GeneID'] = pandas.to_numeric(var_merged['GeneID'], downcast='integer', errors='coerce')\n", + "\n", + "# replace NaN with 'None'\n", + "var_merged.fillna('None', inplace=True)\n", + "\n", + "# drop duplicates\n", + "var_merged.drop_duplicates(inplace=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Process and Align Disease/Phenotype Identifiers*\n", + "\n", + "The two columns from the `submission_summary` file (i.e., `SubmittedPhenotypeInfo`, `ReportedPhenotypeInfo`) and two columns from the `variant_summary` file (i.e., `PhenotypeIDS`, `PhenotypeList`) that contain disease/phenotype identifier information are unnested and processed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 4/4 [33:54<00:00, 508.65s/it]\n" + ] + } + ], + "source": [ + "# unify concept delimiters\n", + "var_merged['PhenotypeList'] = var_merged['PhenotypeList'].str.replace('|', ';')\n", + "var_merged['PhenotypeIDS'] = var_merged['PhenotypeIDS'].str.replace('|', ';').str.replace(',', ';')\n", + "\n", + "# reformat ReportedPhenotypeInfo to match formatting in variant summary\n", + "var_merged['ReportedPhenotypeInfo'] = var_merged['ReportedPhenotypeInfo'].apply(\n", + " lambda x: ';'.join([';'.join(['MedGen:' + i.split(':')[0], i.split(':')[-1]])\n", + " if i.startswith('C') and not i.endswith('not provided')\n", + " else i.split(':')[-1] if i.startswith('na')\n", + " else i for i in x.split(';')]))\n", + "\n", + "# reformat phenotypeIDS and trim leading whitespace from unnested columns\n", + "var_merged['PhenotypeIDS'] = var_merged['PhenotypeIDS'].apply(\n", + " lambda x: ';'.join(['MONDO:' + i.split(':')[-1] if i.startswith('MONDO') \n", + " else 'HP:' + i.split(':')[-1] if i.startswith('Human Phenotype')\n", + " else i for i in x.split(';')]))\n", + "\n", + "# explode the columns\n", + "cols = ['PhenotypeList', 'PhenotypeIDS', 'SubmittedPhenotypeInfo', 'ReportedPhenotypeInfo']\n", + "for col in tqdm(cols): var_merged = var_merged.assign(**{col: var_merged[col].str.split(';')}).explode(col)\n", + " \n", + "# combine columns and keep only unique concepts\n", + "var_merged['PhenotypeString'] = var_merged['SubmittedPhenotypeInfo'] + ';' + var_merged['PhenotypeList']\n", + "var_merged['PhenotypeString'] = var_merged['PhenotypeString'].apply(lambda x: ';'.join(pandas.unique(x.split(';'))))\n", + "var_merged['PhenotypeID'] = var_merged['ReportedPhenotypeInfo'] + ';' + var_merged['PhenotypeIDS']\n", + "var_merged['PhenotypeID'] = var_merged['PhenotypeID'].apply(lambda x: ';'.join(pandas.unique(x.split(';'))))\n", + "# drop columns that are no longer needed\n", + "drop_list = ['PhenotypeList', 'PhenotypeIDS', 'SubmittedPhenotypeInfo', 'ReportedPhenotypeInfo']\n", + "var_merged = var_merged.drop(drop_list, axis=1).drop_duplicates()\n", + "\n", + "# explode phenotype columns and drop duplicates\n", + "cols = ['PhenotypeString', 'PhenotypeID']\n", + "for col in tqdm(cols): var_merged = var_merged.assign(**{col: var_merged[col].str.split(';')}).explode(col)\n", + "\n", + "# create a single phenotype variable, keep only unique concepts, and drop unneeded columns\n", + "var_merged['Phenotype'] = var_merged['PhenotypeString'] + ';' + var_merged['PhenotypeID']\n", + "var_merged['Phenotype'] = var_merged['Phenotype'].apply(lambda x: ';'.join(pandas.unique(x.split(';'))))\n", + "# drop columns that are no longer needed\n", + "drop_list2 = ['PhenotypeString', 'PhenotypeID']\n", + "var_merged = var_merged.drop(drop_list2, axis=1)\n", + "\n", + "# explode final phenotype column\n", + "var_merged = var_merged.assign(**{'Phenotype': var_merged['Phenotype'].str.split(';')}).explode('Phenotype')\n", + "\n", + "# lower case string phenotypes\n", + "var_merged['Phenotype'] = var_merged['Phenotype'].apply(lambda x: x.lower() if ':' not in x else x)\n", + "\n", + "# replace not provided identifiers with ''\n", + "var_merged['Phenotype'] = var_merged['Phenotype'].str.replace('MedGen:CN517202', 'Not Provided')\n", + "var_merged['Phenotype'] = var_merged['Phenotype'].str.replace('CN517202:not provided', 'Not Provided')\n", + "var_merged['Phenotype'] = var_merged['Phenotype'].str.replace('not provided', 'Not Provided')\n", + "\n", + "# drop duplicates\n", + "var_merged.drop_duplicates(inplace=True)\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(var_merged)))\n", + "var_merged.head(n=5)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['MONDO:MONDO:0013342,MedGen:C3150901,OMIM:613647,Orphanet:306511'] ['Spastic paraplegia 48, autosomal recessive']\n" + ] + } + ], + "source": [ + "df = var_summary_update[var_summary_update['VariationID'] == 2]\n", + "print(list(df['PhenotypeIDS']), list(df['PhenotypeList']))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['C3150901:Spastic paraplegia 48, autosomal recessive'] ['SPASTIC PARAPLEGIA 48, AUTOSOMAL RECESSIVE']\n" + ] + } + ], + "source": [ + "df = submission_summary[submission_summary['VariationID'] == 2]\n", + "print(list(df['ReportedPhenotypeInfo']), list(df['SubmittedPhenotypeInfo']))" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Not Provided', 'MONDO:0013342', 'spastic paraplegia 48, autosomal recessive', 'MedGen:C3150901', 'OMIM:613647', 'Orphanet:306511', 'spastic paraplegia 48, autosomal recessive', 'MedGen:C3150901', 'Not Provided']\n" + ] + } + ], + "source": [ + "df = var_merged[var_merged['VariationID'] == 2]\n", + "print(list(df['Phenotype']))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "[**`disease_names`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names)\n", + "\n", + "> Tab-delimited file with the following 7 fields:\n", + ">\n", + "> - <u>DiseaseName</u>: The name preferred by GTR and ClinVar \n", + "> - <u>SourceName</u>: Sources that also use this preferred name \n", + "> - <u>ConceptID</u>: The identifier assigned to a disorder associated with this gene. If the value starts with a C and is followed by digits, the ConceptID is a value from UMLS; if a value begins with CN, it was created by NCBI-based processing \n", + "> - <u>SourceID</u>: Identifier used by the source reported in column 2 (SourceName) \n", + "> - <u>DiseaseMIM</u>: MIM number for the condition \n", + "> - <u>LastUpdated</u>: Last time this record was modified by NCBI staff \n", + "> - <u>Category</u>: Category of disease (as reported in ClinVar's XML), one of: \n", + "> - Blood group\n", + "> - Disease\n", + "> - Finding\n", + "> - Named protein variant\n", + "> - Pharmacological response\n", + "> - phenotype instruction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names'\n", + "if not os.path.exists(unprocessed_data_location + 'disease_names'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "\n", + "# load data\n", + "disease_names = pandas.read_csv(unprocessed_data_location + 'disease_names',\n", + " header=0, delimiter='\\t', low_memory=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# replace NaN and \"-\" with 'None'\n", + "disease_names.fillna('None', inplace=True)\n", + "disease_names = disease_names.replace('na', 'None')\n", + "disease_names = disease_names.replace('-', 'None')\n", + "\n", + "# remove rows without a concept id\n", + "disease_names = disease_names[disease_names['ConceptID'] != 'None']\n", + "\n", + "# reformat ConceptId and SourceID\n", + "disease_names['ConceptID'] = disease_names['ConceptID'].apply(lambda x: 'MedGen:' + x.split(':')[0]\n", + " if x.startswith('C') else x.split(':')[-1])\n", + "disease_names['SourceID'] = disease_names['SourceID'].apply(lambda x: 'Orphanet:' + x.split('ORPHA')[1]\n", + " if x.startswith('ORPHA') else x)\n", + "\n", + "# convert date format\n", + "disease_names['LastModified'] = disease_names['LastModified'].str.replace('None', '')\n", + "disease_names['LastModified'] = pandas.to_datetime(disease_names['LastModified'])\n", + "disease_names['LastModified'] = disease_names['LastModified'].dt.strftime('%B %d, %Y')\n", + "disease_names['LastModified'].fillna('None', inplace=True)\n", + "\n", + "# rename variables\n", + "disease_names.rename(columns={'#DiseaseName': 'DiseaseName'}, inplace=True)\n", + "\n", + "# remove unneeded variables\n", + "drop_list = ['DiseaseMIM']\n", + "disease_names = disease_names.drop(drop_list, axis = 1).drop_duplicates()\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(disease_names)))\n", + "disease_names.head(n=5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "_Preprocess Data_" + "_Merge and Process Data Sources_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Expand and Enhance Disease and Phenotype Identifiers*\n", + "\n", + "The first step is to try and align as many phenotypes to valid identifiers as possible. To do this, we use the `disease_names` data processed in the prior step to join lower-cased phenotype strings in the `submission_summary.SubmittedPhenotypeInfo` columns (respectively) with strings in the `disease_names.DiseaseName` column." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# first try to address disease naming issue between the submission_summary and \n", + "\n", + "\n", + "# merge again, but this time on the provided source and concept identifiers\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "#### Metadata Files <a class=\"anchor\" id=\"metadata-files\"></a>\n", + "***\n", + "\n", + "*Data Files:* \n", + "- [`var_citations.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt) \n", + "- [`allele_gene.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz) \n", + "- [`gene_specific_summary.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt) \n", + "- [`gene_condition_source_id`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id) \n", + "\n", + "*Processing Details* \n", + "<u>Step 1</u>: The first step is down the `variant_summary.txt.gz`, `submission_summary.txt.gz`, and `disease_names` files. After downloading, the files are cleaned to handle missing data, unneeded variables are removed, identifiers and date fields are cleaned and reformatted, and rows without disease/phenotype identifiers are removed (i.e., [`MedGen:CN517202`](https://www.ncbi.nlm.nih.gov/medgen/CN517202)). \n", + "\n", + "<u>Step 2</u>: Merge the `submission_summary.txt.gz`, and `disease_names` files to try and recover phenotype entries that were initially submitted as a string, but have no identifier." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "[**`var_citations.txt`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt)\n", + "\n", + "> A tab-delimited report of citations associated with data in ClinVar, connected to the AlleleID, the VariationID, and either rs# from dbSNP or nsv in dbVar.\n", + ">\n", + "> - <u>AlleleID</u>: integer value as stored in the AlleleID field in ClinVar \n", + "> - <u>VariationID</u>: The identifier ClinVar uses to anchor its default display \n", + "> - <u>rs</u>: rs identifier from dbSNP, null if missing \n", + "> - <u>nsv</u>: nsv identifier from dbVar, null if missing \n", + "> - <u>citation_source</u>: The source of the citation, either PubMed, PubMedCentral, or the NCBI Bookshelf \n", + "> - <u>citation_id</u>: The identifier used by that source " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'var_citations.txt'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "\n", + "# load data\n", + "var_citations = pandas.read_csv(unprocessed_data_location + 'var_citations.txt',\n", + " header=0, delimiter='\\t', low_memory=False)" ] }, { @@ -3768,22 +5035,257 @@ "outputs": [], "source": [ "# replace NaN with 'None'\n", - "clinvar_data.fillna('None', inplace=True)\n", + "var_citations.fillna('None', inplace=True)\n", + "var_citations = var_citations.replace('na', 'None')\n", "\n", - "# explode nested data\n", - "explode_df_clinvar = explodes_data(clinvar_data.copy(), ['PhenotypeIDS'], ';')\n", - "explode_df_clinvar = explodes_data(explode_df_clinvar.copy(), ['PhenotypeIDS'], ',')\n", + "# replace cells that contain \"-\" with 'None'\n", + "var_citations = var_citations.replace('-', 'None')\n", "\n", - "# edit column formatting\n", - "explode_df_clinvar['PhenotypeIDS'].replace('Orphanet:ORPHA','ORPHA:', inplace=True, regex=True)\n", - "explode_df_clinvar['PhenotypeIDS'].replace('Human Phenotype Ontology:HP:','HP_', inplace=True, regex=True)\n", + "# handle rs ids that may be coded as -1\n", + "var_citations = var_citations[var_citations['rs'] != -1]\n", "\n", - "# write data\n", - "explode_df_clinvar.to_csv(processed_data_location + 'CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt', header=True, sep='\\t', encoding='utf-8', index=False)\n", + "# convert rs id to integer\n", + "var_citations['rs'] = var_citations['rs'].str.replace('None', '00000')\n", + "var_citations['rs'] = var_citations['rs'].astype(int)\n", + "var_citations['rs'] = var_citations['rs'].str.replace(0000, 'None')\n", + "\n", + "# rename variables\n", + "var_citations.rename(columns={'#AlleleID': 'AlleleID',\n", + " 'rs': 'RS# (dbSNP)'}, inplace=True)\n", + "\n", + "# remove unneeded variables\n", + "drop_list = ['nsv']\n", + "var_citations = var_citations.drop(drop_list, axis = 1).drop_duplicates()\n", "\n", "# print row count and preview data\n", - "print('There are {edge_count} variant edges'.format(edge_count=len(explode_df_clinvar)))\n", - "explode_df_clinvar.head(n=5)" + "print('There are {edge_count} edges'.format(edge_count=len(var_citations)))\n", + "var_citations.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "[**`allele_gene.txt.gz`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz)\n", + "\n", + "> Reports per ClinVar's AlleleID, the genes that are related to that gene and how they are related.\n", + ">\n", + "> - <u>AlleleID</u>: the integer identifier assigned by ClinVar to each simple allele\n", + "> - <u>GeneID</u>: integer, GeneID in NCBI's Gene database \n", + "> - <u>Symbol</u>: character, Symbol preferred in NCBI's Gene database. Is the symbol from HGNC when available \n", + "> - <u>Name</u>: character, full name of the gene \n", + "> - <u>GenesPerAlleleID</u>: integer, number of genes related to the allele \n", + "> - <u>Category</u>: character, type of allele-gene relationship. The values for category are:\n", + "> - <u>asserted, but not computed</u>: Submitted as related to a gene, but not within the location of that gene on the genome \n", + "> - <u>genes overlapped by variant</u>: The gene and variant overlap \n", + "> - <u>near gene, downstream</u>: Outside the location of the gene on the genome, within 5 kb \n", + "> - <u>near gene, upstream</u>: Outside the location of the gene on the genome, within 5 kb \n", + "> - <u>within multiple genes by overlap</u>: The variant is within genes that overlap on the genome. Includes introns \n", + "> - <u>within single gene</u>: The variant is in only one gene. Includes introns \n", + "> - <u>Source</u>: character, was the relationship submitted or calculated? " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'allele_gene.txt'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "\n", + "# load data\n", + "allele_gene = pandas.read_csv(unprocessed_data_location + 'allele_gene.txt',\n", + " header=0, delimiter='\\t', low_memory=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# replace NaN with 'None'\n", + "allele_gene.fillna('None', inplace=True)\n", + "allele_gene = allele_gene.replace('na', 'None')\n", + "\n", + "# replace cells that contain \"-\" with 'None'\n", + "allele_gene = allele_gene.replace('-', 'None')\n", + "\n", + "# handle gene ids that may be coded as -1\n", + "allele_gene = allele_gene[allele_gene['GeneID'] != -1]\n", + "\n", + "# rename variables\n", + "allele_gene.rename(columns={'#AlleleID': 'AlleleID'}, inplace=True)\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(allele_gene)))\n", + "allele_gene.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "[**`gene_specific_summary.txt`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt)\n", + "\n", + "> A tab-delimited report, for each gene, of the number of submissions and the number of different variants (alleles).\n", + "> Because some variant-gene relationships are submitted, and some are calculated from overlapping annotation, in January of 2015, the report was modified to indicate when the gene-variant relationship was submitted.\n", + "> \n", + "> - <u>Symbol</u>: Gene symbol (if officially named, from HGNC, else from NCBI's Gene database) \n", + "> - <u>GeneID</u>: Unique identifier from NCBI's Gene database \n", + "> - <u>Total_submissions</u>: Total submissions to ClinVar with variants in/overlapping this gene \n", + "> - <u>Total_alleles</u>: Number of alleles submitted to ClinVar for this gene \n", + "> - <u>Submissions_reporting_this_gene</u>: Subset of the total submissions that also reported the gene \n", + "> - <u>Alleles_reported_Pathogenic_Likely_pathogenic</u>: Number of variants reported as pathogenic or likely pathogenic. Excludes structural variants that may overlap a gene \n", + "> - <u>Gene_MIM_Number</u>: The MIM number for this gene \n", + "> - <u>Number_Uncertain</u>: Submissions with an interpretation of 'Uncertain significance' \n", + "> - <u>Number_with_conflicts</u>: Number of VariationIDs for this gene with conflicting interpretations " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'gene_specific_summary.txt'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "\n", + "# load data\n", + "gene_summary = pandas.read_csv(unprocessed_data_location + 'gene_specific_summary.txt',\n", + " header=0, skiprows=1, delimiter='\\t', low_memory=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# replace NaN with 'None'\n", + "gene_summary.fillna('None', inplace=True)\n", + "gene_summary = gene_summary.replace('na', 'None')\n", + "\n", + "# replace cells that contain \"-\" with 'None'\n", + "gene_summary = gene_summary.replace('-', 'None')\n", + "\n", + "# handle gene ids that may be coded as -1\n", + "gene_summary = gene_summary[gene_summary['GeneID'] != -1]\n", + "\n", + "# rename variables\n", + "gene_summary.rename(columns={'#Symbol': 'Symbol'}, inplace=True)\n", + "\n", + "# remove unneeded variables\n", + "drop_list = ['Gene_MIM_number']\n", + "gene_summary = gene_summary.drop(drop_list, axis = 1).drop_duplicates()\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(gene_summary)))\n", + "gene_summary.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "[**`gene_condition_source_id`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id)\n", + "\n", + "> Tab-delimited file with the following fields:\n", + "> \n", + "> - <u>GeneID</u>: The NCBI GeneID \n", + "> - <u>AssociatedGenes</u>: The preferred symbol corresponding to the GeneID for the gene reported to be causative for this disorder \n", + "> - <u>RelatedGenes</u>: The preferred symbol corresponding to any gene that may contribute to a disorder. This column is null for monogenic disorders, but will be reported for broader concepts. For example, ABCA4 is reported as an AssociatedGene for Retinitis pigmentosa 19, but a related gene for Retinitis pigmentosa \n", + "> - <u>ConceptID</u>: The identifier assigned to a disorder associated with this gene. If the value starts with a C and is followed by digits, the ConceptID is a value from UMLS; if a value begins with CN, it was created by NCBI-based processing \n", + "> - <u>DiseaseName</u>: Full name for the condition \n", + "> - <u>SourceName</u>: Sources that use this name \n", + "> - <u>SourceID</u>: The identifier used by this source \n", + "> - <u>DiseaseMIM</u>: MIM number for the condition \n", + "> - <u>LastUpdated</u>: Last time this record was modified by NCBI staff " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id'\n", + "if not os.path.exists(unprocessed_data_location + 'gene_condition_source_id'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "\n", + "# load data\n", + "gene_cond_src = pandas.read_csv(unprocessed_data_location + 'gene_condition_source_id',\n", + " header=0, delimiter='\\t', low_memory=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# replace NaN with 'None'\n", + "gene_cond_src.fillna('None', inplace=True)\n", + "gene_cond_src = gene_cond_src.replace('na', 'None')\n", + "\n", + "# replace cells that contain \"-\" with 'None'\n", + "gene_cond_src = gene_cond_src.replace('-', 'None')\n", + "\n", + "# handle gene ids that may be coded as -1\n", + "gene_cond_src = gene_cond_src[gene_cond_src['#GeneID'] != -1]\n", + "\n", + "# handle gene ids that may be coded as -1\n", + "gene_cond_src = gene_cond_src[gene_cond_src['ConceptID'] != -1]\n", + "gene_cond_src = gene_cond_src[gene_cond_src['ConceptID'] != 'None']\n", + "\n", + "# reformat ReportedPhenotypeInfo to match formatting in variant summary\n", + "gene_cond_src['ConceptID'] = gene_cond_src['ConceptID'].apply(lambda x: 'MedGen:' + x.split(':')[0]\n", + " if x.startswith('C') else x.split(':')[-1])\n", + "\n", + "# convert date format\n", + "gene_cond_src['LastUpdated'] = gene_cond_src['LastUpdated'].str.replace('None', '')\n", + "gene_cond_src['LastUpdated'] = pandas.to_datetime(gene_cond_src['LastUpdated'])\n", + "gene_cond_src['LastUpdated'] = gene_cond_src['LastUpdated'].dt.strftime('%B %d, %Y')\n", + "gene_cond_src['LastUpdated'].fillna('None', inplace=True)\n", + "\n", + "# rename variables\n", + "gene_cond_src.rename(columns={'#GeneID': 'GeneID'}, inplace=True)\n", + "\n", + "# remove unneeded variables\n", + "drop_list = ['DiseaseMIM']\n", + "gene_cond_src = gene_cond_src.drop(drop_list, axis = 1).drop_duplicates()\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(gene_cond_src)))\n", + "gene_cond_src.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_Merge and Process Data Sources_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# first try to address disease naming issue between the submission_summary and \n" ] }, { @@ -4231,7 +5733,7 @@ "# load data\n", "reactome_pathways2 = pandas.read_csv(unprocessed_data_location + 'gene_association.reactome', header=None, delimiter='\\t', skiprows=3, low_memory=False)\n", "reactome_pathways2 = reactome_pathways2.loc[reactome_pathways2[12].apply(lambda x: x == 'taxon:9606')]\n", - "reactome_pathways2[5].replace('REACTOME:','', inplace=True, regex=True) \n", + "reactome_pathways2[5].str.replace('REACTOME:','', inplace=True, regex=True) \n", "\n", "# reactome CHEBI data\n", "url = 'https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt'\n", From 7d1560f561d6b0f0c2dcb3ceedb30a263e2918e0 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 08:38:01 -0700 Subject: [PATCH 045/112] adding clinvar datasets --- builds/data_to_download.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/builds/data_to_download.txt b/builds/data_to_download.txt index b2029b1c..9b606146 100755 --- a/builds/data_to_download.txt +++ b/builds/data_to_download.txt @@ -38,7 +38,15 @@ genomic_sequence_ontology_mappings.xlsx, https://storage.googleapis.com/pheknowl # protein ontology consortium sparql query results human_pro_classes.html, https://sparql.proconsortium.org/virtuoso/sparql?query=PREFIX+obo%3A+%3Chttp%3A%2F%2Fpurl.obolibrary.org%2Fobo%2F%3E%0D%0A%0D%0ASELECT+%3FPRO_term%0D%0AFROM+%3Chttp%3A%2F%2Fpurl.obolibrary.org%2Fobo%2Fpr%3E%0D%0AWHERE+%7B%0D%0A+++++++%3FPRO_term+rdf%3Atype+owl%3AClass+.%0D%0A+++++++%3FPRO_term+rdfs%3AsubClassOf+%3Frestriction+.%0D%0A+++++++%3Frestriction+owl%3AonProperty+obo%3ARO_0002160+.%0D%0A+++++++%3Frestriction+owl%3AsomeValuesFrom+obo%3ANCBITaxon_9606+.%0D%0A%0D%0A+++++++%23+use+this+to+filter-out+things+like+hgnc+ids%0D%0A+++++++FILTER+%28regex%28%3FPRO_term%2C%22http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2F*%22%29%29+.%0D%0A%7D&format=text%2Fhtml&debug= # clinvar variant-diseases and phenotypes -ClinVarFullRelease.xml, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/xml/ClinVarFullRelease_00-latest.xml.gz +variant_summary.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz +submission_summary.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz +disease_names, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names +var_citations.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt +allele_gene.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz +gene_specific_summary.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt +gene_condition_source_id, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id +# human phenotype and disease mapping files +MGCONSO.RRF, https://ftp.ncbi.nlm.nih.gov/pub/medgen/MGCONSO.RRF.gz # uniprot protein-cofactor and protein-catalyst uniprot-cofactor-catalyst.tab, https://www.uniprot.org/uniprot/?query=&fil=organism%3A%22Homo%20sapiens%20(Human)%20%5B9606%5D%22&columns=id%2Creviewed%2Centry%20name%2Cdatabase(PRO)%2Cchebi(Cofactor)%2Cchebi(Catalytic%20activity)&format=tab From c59bb1cff06ae13ceb14e8d87ea3242c2e832524 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 13:00:01 -0700 Subject: [PATCH 046/112] adding file header and editing content --- resources/resource_info.txt | 98 ++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 34 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index ab937cc4..7648af8c 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -1,34 +1,64 @@ -chemical-disease|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None -chemical-gene|;MESH_;NCBIGene_|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|http://www.ncbi.nlm.nih.gov/gene/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); -chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Biological Process -chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Cellular Component -chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Molecular Function -chemical-pathway|;CHEBI_;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|None|None|5;==;Homo sapiens -chemical-phenotype|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None -chemical-protein|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); -chemical-rna|;MESH_;|class-entity|RO_0002434|http://purl.obolibrary.org/obo/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); -disease-phenotype|:;;HP_|class-class|RO_0002200|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|2;!=;NOT -gene-disease|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|6;!=;group -gene-gene|;NCBIGene;NCBIGene|entity-entity|RO_0002435|http://www.ncbi.nlm.nih.gov/gene/|http://www.ncbi.nlm.nih.gov/gene/|t|0;1|0:./resources/processed_data/OTHER_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None -gene-pathway|:;NCBIGene;|entity-entity|RO_0000056|http://www.ncbi.nlm.nih.gov/gene/|https://reactome.org/content/detail/|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); -gene-phenotype|;NCBIGene;|entity-class|RO_0003302|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|6;!=;group -gene-protein|;NCBIGene;|entity-class|RO_0002205|http://www.ncbi.nlm.nih.gov/gene/|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding -gene-rna|;NCBIGene;|entity-entity|RO_0002511|http://www.ncbi.nlm.nih.gov/gene/|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|t|0;1|None|None|None -gobp-pathway|:;GO_;|class-entity|RO_0009501|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -pathway-gocc|:;;GO_|entity-class|RO_0002180|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -pathway-gomf|:;;GO_|entity-class|RO_0000085|https://reactome.org/content/detail/|http://purl.obolibrary.org/obo/|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -protein-anatomy|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;anatomy -protein-catalyst|;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;1|None|None|None|None -protein-cell|;;|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;cell line -protein-cofactor|;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|0;1|None|None|None|None -protein-gobp|:;;GO_|class-class|RO_0000056|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein -protein-gocc|:;;GO_|class-class|RO_0001025|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein -protein-gomf|:;;GO_|class-class|RO_0000085|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein -protein-pathway|;;|class-entity|RO_0000056|http://purl.obolibrary.org/obo/|https://reactome.org/content/detail/|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens -protein-protein|9606.;;|class-class|RO_0002436|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|None|None -rna-anatomy|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy -rna-cell|;;|entity-class|RO_0001025|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line -rna-protein|;;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding -variant-disease|:;rs;|entity-class|RO_0003302|https://www.ncbi.nlm.nih.gov/snp/|http://purl.obolibrary.org/obo/|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc -variant-gene|;rs;NCBIGene|entity-entity|RO_0002566|https://www.ncbi.nlm.nih.gov/snp/|http://www.ncbi.nlm.nih.gov/gene/|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc -variant-phenotype|:;rs;|entity-class|RO_0003302|https://www.ncbi.nlm.nih.gov/snp/|http://purl.obolibrary.org/obo/|t|9;12|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc \ No newline at end of file +###################################################################################################################################################################################### +#### resource_info.txt (last updated: December 27, 2021) +### Each column is separated by a pipe (i.e., "|") and includes the following: +# EdgeType: A string label for an edge (node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files. +# IdentifierPrefixInformation: Three ";"-separated items used to update a prefix-identifier pair (e.g., :;GO_;GO_). The first item contains the character that separates +# existing prefixes and identifiers (e.g. ":" in GO:1283834). The second item contains the current prefix and the third item contains the +# new prefix (i.e. 'GO_' and 'GO_'). If the existing prefix is correct, type ";;". +# NodeDataTypes: A label of "class" or "entity" for each node in an edge separated by "-" (e.g., "class-class"). The "class" label represents nodes from +# ontologies and "entity" represents nodes from other data sources. +# Relation: A Relation Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056). +# Delimiter: A character used to split rows from an input data source into columns (e.g., "t" for tab-delimited data or "," for comma-delimited data). +# ColumnIndexes: Two-column indexes separated by ";" (e.g., "0;4" for the first and third columns in the input data source). +# IdentifierMaps: A string of mapping information for each node in an edge. For example, the string "2:mapping_file_1.txt;4:mapping_file_2.txt" means that +# the first node requires data contained in the 2nd column of the "mapping_file_1.txt" and the second node requires data from the 4th column +# in the "mapping_file_2.txt" file. +# EvidenceCriteria: Evidence criteria that can be used to filter an input data source (e.g., scores above a certain cut-off). An evidence set is composed of 3 +# pieces of ";"-separated information (e.g., "4;!=;IEA::8;<;0.0001"): +# 1. The index of the column to apply the evidence criteria to (e.g., "4" and "8" in the example above) +# 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" +# in the example above) +# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and "0.0001" in the example above) +# Multiple evidence sets can be passed as demonstrated by the example above, where each set is separated by "::". +# FilterCriteria: Filtering criteria that can be used to filter an input data source (e.g., human proteins). An evidence set is composed of 3 pieces of ";"- +# separated information (e.g., "5;==;P::7;==;9606"): +# 1. The index of the column to apply the evidence criteria to (e.g., "5" and "7" in the example above) +# 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" +# in the example above) +# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above) +# Multiple filtering sets can be passed as demonstrated by the example above, where each set is separated by "::". +###################################################################################################################################################################################### +chemical-disease|:;MESH_;|class-class|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None +chemical-gene|;MESH_;NCBIGene_|class-entity|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); +chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Biological Process +chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Cellular Component +chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Molecular Function +chemical-pathway|;CHEBI_;|class-entity|RO_0000056|t|0;1|None|None|5;==;Homo sapiens +chemical-phenotype|:;MESH_;|class-class|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None +chemical-protein|;MESH_;|class-class|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); +chemical-rna|;MESH_;|class-entity|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); +disease-phenotype|:;;HP_|class-class|RO_0002200|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|2;!=;NOT +gene-disease|;NCBIGene;|entity-class|RO_0003302|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|6;!=;group +gene-gene|;NCBIGene;NCBIGene|entity-entity|RO_0002435|t|0;1|0:./resources/processed_data/OTHER_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None +gene-pathway|:;NCBIGene;|entity-entity|RO_0000056|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); +gene-phenotype|;NCBIGene;|entity-class|RO_0003302|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|6;!=;group +gene-protein|;NCBIGene;|entity-class|RO_0002205|t|0;1|None|None|4;==;protein-coding +gene-rna|;NCBIGene;|entity-entity|RO_0002511|t|0;1|None|None|None +gobp-pathway|:;GO_;|class-entity|RO_0009501|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +pathway-gocc|:;;GO_|entity-class|RO_0002180|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +pathway-gomf|:;;GO_|entity-class|RO_0000085|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +protein-anatomy|;;|class-class|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;anatomy +protein-catalyst|;;|class-class|RO_0002436|t|0;1|None|None|None|None +protein-cell|;;|class-class|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;cell line +protein-cofactor|;;|class-class|RO_0002436|t|0;1|None|None|None|None +protein-gobp|:;;GO_|class-class|RO_0000056|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-gocc|:;;GO_|class-class|RO_0001025|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-gomf|:;;GO_|class-class|RO_0000085|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-pathway|;;|class-entity|RO_0000056|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens +protein-protein|9606.;;|class-class|RO_0002436|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|None|None +rna-anatomy|;;|entity-class|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy +rna-cell|;;|entity-class|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line. +rna-protein|;;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding. +variant-disease|:;rs;|entity-class|RO_0003302|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc. +variant-gene|;rs;NCBIGene|entity-entity|RO_0002566|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc. +variant-phenotype|:;rs;|entity-class|RO_0003302|t|9;12|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc \ No newline at end of file From 6d805385ea1599a2b802c6ebbcf94bfa1e701c3c Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 13:35:33 -0700 Subject: [PATCH 047/112] extended example --- resources/resource_info.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 7648af8c..da7b6913 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -4,7 +4,8 @@ # EdgeType: A string label for an edge (node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files. # IdentifierPrefixInformation: Three ";"-separated items used to update a prefix-identifier pair (e.g., :;GO_;GO_). The first item contains the character that separates # existing prefixes and identifiers (e.g. ":" in GO:1283834). The second item contains the current prefix and the third item contains the -# new prefix (i.e. 'GO_' and 'GO_'). If the existing prefix is correct, type ";;". +# new prefix (i.e. 'GO_' and 'GO_'). If the existing prefix is correct, type ";;". if there is no prefix in the current data, leave the +# item empty and specify the new prefix for the node in the corresponding item location. # NodeDataTypes: A label of "class" or "entity" for each node in an edge separated by "-" (e.g., "class-class"). The "class" label represents nodes from # ontologies and "entity" represents nodes from other data sources. # Relation: A Relation Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056). From 77207f60a43042f207e95bf01119fde9c2990d5c Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 13:36:09 -0700 Subject: [PATCH 048/112] updated script --- generates_dependency_documents.py | 111 +++++++++++++++--------------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/generates_dependency_documents.py b/generates_dependency_documents.py index 26b09a02..1abc66bb 100644 --- a/generates_dependency_documents.py +++ b/generates_dependency_documents.py @@ -32,8 +32,10 @@ class DocumentationMaker(object): def __init__(self, edge_count: int, write_location: str = './resources') -> None: # check edge count - if not isinstance(edge_count, int): raise ValueError('edge_count must be an integer (i.e. "1" not "one").') - else: self.edge_count = edge_count + if not isinstance(edge_count, int): + raise ValueError('edge_count must be an integer (i.e. "1" not "one").') + else: + self.edge_count = edge_count # make sure that the specified location to write data exists if os.path.exists(write_location): @@ -63,7 +65,7 @@ def information_getter(self) -> Tuple[Dict[str, str], Dict[str, str], Dict[str, print('GATHERING INFORMATION FOR EDGE: {count}/{total}'.format(count=edge, total=self.edge_count)) print('#' * 40) - edge_name = input('Please enter the edge type (e.g. "gene-protein", "disease-chemical"): ') + edge_type = input('Please enter the edge type (e.g. "gene-protein", "disease-chemical"): ') print('\n') ont = input('Is one or both of the nodes in edge an ontology? Please enter "one" or "both": ') @@ -76,8 +78,9 @@ def information_getter(self) -> Tuple[Dict[str, str], Dict[str, str], Dict[str, ont_data[ont_edge] = input('Provide an owl or obo URL for this ontology: ') print('\n') - data_type = input('Provide the data types for each node in the edge (e.g. "class" or "entity" (for ' - 'non-class data) each node in the edge separated by "-" --> "class-entity"): ') + node_data_types = input('Provide the data types for each node in the edge (e.g. "class" or "entity" (for ' + 'data that is not from an ontology) each node in the edge separated by "-" --> ' + '"class-entity"): ') print('\n') delimiter = input('Provide the character used to split each row into columns (e.g. "t" or ","): ') @@ -111,29 +114,23 @@ def information_getter(self) -> Tuple[Dict[str, str], Dict[str, str], Dict[str, '"RO_0000056"): ') print('\n') - subj_uri = input('Provide the Universal Resource Identifier that will be connected to the subject node (' - '(e.g. "http://purl.obolibrary.org/obo/"): ') - print('\n') - - obj_uri = input('Provide the Universal Resource Identifier that will be connected to the object node: ') - print('\n') - - source_label = input('Source Identifier Formatting (i.e. GO:12838340, when we need ' - 'GO_12838340).\n\nProvide the following 3 items:\n(1) Character to split existing ' - 'source labels (e.g. ":" in GO:1283834);\n(2) New label to replace existing label) ' - 'for subject node (e.g. "GO_");\n(3) New label to replace existing label) for ' - 'object node (e.g. GO_).\n\nEnter each item separated by ";". If the existing label ' - 'is correct, press "enter": ') or ';;' + identifier_prefix_information = input('Source Identifier Formatting (i.e., GO:12838340, when we need ' + 'GO_12838340).\n\nProvide the following 3 items:\n(1) Character to ' + 'split existing CURIE (e.g., ":" in GO:1283834);\n(2) New subject ' + 'prefix to replace existing one (e.g. "GO_");\n(3) New object ' + 'prefix to replace existing one (e.g., GO_).\n\nEnter each item ' + 'separated by ";". If the existing prefix is correct, press "enter": ' + ') or ";;"') print('\n') # add edge data to dictionary - resource_data[edge_name] = '{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}|{8}|{9}'.format(source_label, data_type, - edge_relation, subj_uri, - obj_uri, delimiter, col_idx, - id_maps, evi_crit, filt_crit) + resource_data[edge_type] = '{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}'.format(identifier_prefix_information, + node_data_types, edge_relation, + delimiter, col_idx, id_maps, + evi_crit, filt_crit) # get edge data sources - edge_data[edge_name] = input('Provide a URL or file path to data used to create this edge: ') + edge_data[edge_type] = input('Provide a URL or file path to data used to create this edge: ') return resource_data, ont_data, edge_data @@ -158,7 +155,6 @@ def writes_out_document(self, data: Dict[str, str], delimiter: str, filename: st def main(): - # print initial message for user print('\n\n' + '***' * 50) print('INPUT DOCUMENT BUILDER\n\nThis program will help you generate the input documentation needed to run ' @@ -166,37 +162,44 @@ def main(): 'you create three documents: (1) resource_info.txt; (2) ontology_source_info.txt; and (3) ' 'edge_source_info.txt.\nAn example of the data this program expects to find within each of these ' 'documents is shown below:\n\n(1) resource_info.txt: This document represents each edge type as a single ' - '"|" delimited string and contains a total of 11 items:\n\t(1) Edge Type: A string containing a "-" ' - 'delimited edge label (node1-node2)\n\t(2) Source Labels: 3 ";"-delimited strings (e.g. ":;GO_;GO_)":\n\t\t' - '-the character to split existing labels (e.g. ":" in GO:1283834)\n\t\t-a new label for the subject ' - 'node\n\t\t-a new label for the object node. If the existing label is correct, use ";;";\n\t(3) Data ' - 'Type: A label of "class", "entity" ( fornon-ontology data) provided for each node and separated by "-" (' - 'e.g. "class-class", "class-entity", "entity-class");\n\t(4) Edge Relation: A Relation Ontology identifier to' - 'be used as an edge between the nodes (e.g. "RO_0000056")\n\t(5) Subject URI: A Universal Resource Identifier' - ' that will be connected to the subject node in the Edge Type (e.g. "http://purl.uniprot.org/geneid/");\n\t' - '(6) Object URI that will be connected to the object node in the Edge Type (e.g. ' - '"http://purl.obolibrary.org/obo/");\n\t(7) Delimiter: A character used to split input text rows into ' - 'columns (e.g. "t" or ",");\n\t(8) Column Indices: two column indices separated by ";" (e.g. 0;4 for the ' - 'first and third columns);\n\t(9) Identifier Maps: A string indicating the column index in the input data' - ' source needing identifier mapping and a file pointing to mapping data, for example:\n' - '\t\t"2:./resources/processed_data/mapping_file_1.txt;4:./resources/processed_data/mapping_file_2.txt" ' - 'means:\n\t\t\t-mapping data from the first node in the edge to the 0th column in ' - '"mapping_file_1.txt"\n\t\t\t-mapping data from the second node in the edge to the 4th column in ' - '"mapping_file_2.txt");\n\t(10) Evidence Criteria: Sets of 3 "::"-separated items, where each set is ' - 'composed of three pieces of ";"-separated information (e.g. "4;!=;IEA::8;<;0.0001" - means:\n\t\t-filter ' - 'the 4th column to keep rows that do not contain "IEA"\n\t\t-filter the 8th column to keep rows with a ' - 'value less than "0.0001");\n\t(11) Filter Criteria: Sets of 3 "::"-separated items, where each set is ' - 'composed of three pieces of ";"-separated information (e.g. "5;==;P::7;==;9606" - means:\n\t\t-filter the ' - '5th column to only include rows with "P"\n\t\tfilter the 7th column to only include rows containing ' - '"99606").\n\n\tAn example line from the resource_info.txt file is shown ' - 'below:\n\t\tchemical-gene|;MESH_;|class-class|;MESH_;|class-class|RO_0002434|http://purl.obolibrary.org' - '/obo/\n\t\t|http://purl.uniprot.org/geneid/|#|t|1;4|0:./resources/data_maps/MESH_CHEBI_MAP.txt|None|7' - ';==;9606\n\n(2) ontology_source_info.txt: This document contains a ","-delimited line for each ontology ' - 'source used, for example:\n\t"chemical, http://purl.obolibrary.org/obo/chebi.owl"\n\t"gene, ' - 'http://purl.obolibrary.org/obo/so.owl"\n\n(3) edge_source_info.txt: This document contains a ",' - '"-delimited line for each edge data source, for example:\n\t"chemical-gene, ' + '"|" delimited string and contains a total of 9 items:\n\t(1) EdgeType: A string label for an edge ' + '(node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files' + '\n\t(2) IdentifierPrefixInformation: Three ";"-separated items used to update a prefix-identifier pair ' + '(e.g., :;GO_;GO_). The first item contains the character that separates existing\n\t\tprefixes and ' + 'identifiers (e.g. ":" in GO:1283834). The second item contains the current prefix and the third item ' + 'contains the new prefix (i.e. "GO_" and "GO_"). If the existing\n\t\tprefix is correct, type ";;". if there ' + 'is no prefix in the current data, leave the item empty and specify the new prefix for the node in the ' + 'corresponding item location;\n\t(3) NodeDataTypes: A label of "class" or "entity" for each node in an edge ' + 'separated by "-" (e.g., "class-class"). The "class" label\n\t\trepresents nodes from ontologies and ' + '"entity" represents nodes from other data sources;\n\t(4) Relation: A Relation Ontology (' + 'http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056)\n\t(5) Delimiter: A character used to ' + 'split rows from an input data source into columns (e.g., "t" for tab-delimited data or "," for ' + 'comma-delimited data);\n\t(6) ColumnIndexes: Two-column indexes separated by ";" (e.g., "0;4" for the first ' + 'and third columns in the input data source);\n\t(7) IdentifierMaps: A string of mapping information for ' + 'each node in an edge. For example, the string "2:mapping_file_1.txt;4:mapping_file_2.txt" means that the ' + 'first node require\n\t\tdata contained in the 2nd column of the "mapping_file_1.txt" and the second node ' + 'requires data from the 4th column in the "mapping_file_2.txt" file;\n\t(8) EvidenceCriteria: Evidence ' + 'criteria that can be used to filter an input data source (e.g., scores above a certain cut-off). An ' + 'evidence set is composed of 3 pieces of ";"\n\t\t-separated information. Multiple filtering sets can be ' + 'passed, where each set is separated by "::". Consider the following example: ' + '"4;!=;IEA::8;<;0.0001"):\n\t\t1. The index of the column to apply the evidence criteria to ' + '(e.g., "4" and "8" in the example above)\n\t\t2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", ' + '"in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" in the example above).' + ';\n\t(9) Filtering criteria that can be used to filter an input data source (e.g., human proteins). ' + 'An evidence set is composed of 3 pieces of ";"-separated information.\n\t\tMultiple filtering sets can be ' + 'passed as demonstrated by the example above, where each set is separated by "::". Consider the following ' + 'example: "5;==;P::7;==;9606"):\n\t\t1. The index of the column to apply the evidence criteria to ' + '(e.g., "5" and "7" in the example above)\n\t\t2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", ' + '"in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" in the example above)\n\t\t' + '3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above).' + '\n\n\tAn example line from the resource_info.txt file is shown below:\n\t\tchemical-gene|;MESH_;|class-class' + '|;MESH_;|class-class|RO_0002434|#|t|1;4|0:./resources/data_maps/MESH_CHEBI_MAP.txt|None|7;==;9606\n\n(2)' + ' ontology_source_info.txt: This document contains a ","-delimited line for each ontology source used, ' + 'for example:\n\t"chemical, http://purl.obolibrary.org/obo/chebi.owl"\n\t"gene, ' + 'http://purl.obolibrary.org/obo/so.owl"\n\n(3) edge_source_info.txt: This document contains a ","-delimited ' + 'line for each edge data source, for example:\n\t"chemical-gene, ' 'http://ctdbase.org/reports/CTD_chem_gene_ixns.tsv.gz"\n\nIf you would like more information on the ' - 'dependency documents need to run PheKnowLator, please visit the following Wiki page: ' + 'dependency documents need to run PheKnowLator, please visit the following Wiki page:\n' 'https://github.com/callahantiff/PheKnowLator/wiki/Dependencies.') print('***' * 50 + '\n') From 4d4fab3dca4f4a8230ec891ca3a013cab8d4d07e Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 13:41:17 -0700 Subject: [PATCH 049/112] improved spacing --- generates_dependency_documents.py | 37 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/generates_dependency_documents.py b/generates_dependency_documents.py index 1abc66bb..0459cd05 100644 --- a/generates_dependency_documents.py +++ b/generates_dependency_documents.py @@ -159,8 +159,8 @@ def main(): print('\n\n' + '***' * 50) print('INPUT DOCUMENT BUILDER\n\nThis program will help you generate the input documentation needed to run ' 'PheKnowLator by asking specific information about each edge type in the knowledge graph.\nIt will help ' - 'you create three documents: (1) resource_info.txt; (2) ontology_source_info.txt; and (3) ' - 'edge_source_info.txt.\nAn example of the data this program expects to find within each of these ' + 'you create three documents:\n\t\t(1) resource_info.txt\n\t\t(2) ontology_source_info.txt\n\t\t(3) ' + 'edge_source_info.txt\nAn example of the data this program expects to find within each of these ' 'documents is shown below:\n\n(1) resource_info.txt: This document represents each edge type as a single ' '"|" delimited string and contains a total of 9 items:\n\t(1) EdgeType: A string label for an edge ' '(node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files' @@ -182,26 +182,27 @@ def main(): 'criteria that can be used to filter an input data source (e.g., scores above a certain cut-off). An ' 'evidence set is composed of 3 pieces of ";"\n\t\t-separated information. Multiple filtering sets can be ' 'passed, where each set is separated by "::". Consider the following example: ' - '"4;!=;IEA::8;<;0.0001"):\n\t\t1. The index of the column to apply the evidence criteria to ' - '(e.g., "4" and "8" in the example above)\n\t\t2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", ' + '"4;!=;IEA::8;<;0.0001"):\n\t\t\t1. The index of the column to apply the evidence criteria to ' + '(e.g., "4" and "8" in the example above)\n\t\t\t2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", ' '"in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" in the example above).' - ';\n\t(9) Filtering criteria that can be used to filter an input data source (e.g., human proteins). ' - 'An evidence set is composed of 3 pieces of ";"-separated information.\n\t\tMultiple filtering sets can be ' - 'passed as demonstrated by the example above, where each set is separated by "::". Consider the following ' - 'example: "5;==;P::7;==;9606"):\n\t\t1. The index of the column to apply the evidence criteria to ' - '(e.g., "5" and "7" in the example above)\n\t\t2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", ' - '"in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" in the example above)\n\t\t' - '3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above).' - '\n\n\tAn example line from the resource_info.txt file is shown below:\n\t\tchemical-gene|;MESH_;|class-class' - '|;MESH_;|class-class|RO_0002434|#|t|1;4|0:./resources/data_maps/MESH_CHEBI_MAP.txt|None|7;==;9606\n\n(2)' - ' ontology_source_info.txt: This document contains a ","-delimited line for each ontology source used, ' - 'for example:\n\t"chemical, http://purl.obolibrary.org/obo/chebi.owl"\n\t"gene, ' - 'http://purl.obolibrary.org/obo/so.owl"\n\n(3) edge_source_info.txt: This document contains a ","-delimited ' - 'line for each edge data source, for example:\n\t"chemical-gene, ' + '\n\t\t\t3.The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and "0.0001" in the ' + 'example above);\n\t(9) Filtering criteria that can be used to filter an input data source (e.g., ' + 'human proteins). An evidence set is composed of 3 pieces of ";"-separated information.\n\t\tMultiple ' + 'filtering sets can be passed as demonstrated by the example above, where each set is separated by "::". ' + 'Consider the following example: "5;==;P::7;==;9606"):\n\t\t\t1. The index of the column to apply the ' + 'evidence criteria to (e.g., "5" and "7" in the example above)\n\t\t\t2. The operator (i.e., "==", "!=", ' + '"<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" in ' + 'the example above)\n\t\t\t3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and ' + '"9606" in the example above).\n\n\tAn example line from the resource_info.txt file is shown below:\n\t\t' + 'chemical-gene|;MESH_;|class-class|;MESH_;|class-class|RO_0002434|#|t|1;4|0:./resources/data_maps/' + 'MESH_CHEBI_MAP.txt|None|7;==;9606\n\n(2) ontology_source_info.txt: This document contains a ","-delimited ' + 'line for each ontology source used, for example:\n\t"chemical, http://purl.obolibrary.org/obo/chebi.owl"' + '\n\t"gene, http://purl.obolibrary.org/obo/so.owl"\n\n(3) edge_source_info.txt: This document contains a ' + '","-delimited line for each edge data source, for example:\n\t"chemical-gene, ' 'http://ctdbase.org/reports/CTD_chem_gene_ixns.tsv.gz"\n\nIf you would like more information on the ' 'dependency documents need to run PheKnowLator, please visit the following Wiki page:\n' 'https://github.com/callahantiff/PheKnowLator/wiki/Dependencies.') - print('***' * 50 + '\n') + print('***' * 60 + '\n') # initialize class edge_count = int(input('EDGE COUNT: Enter the number of edge types to create: ')) From 6202f1fd84e1140c1c117bf8cc08a171fe1a6e45 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 13:53:54 -0700 Subject: [PATCH 050/112] making all dependencies "|" delimited --- generates_dependency_documents.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/generates_dependency_documents.py b/generates_dependency_documents.py index 0459cd05..a1aa2783 100644 --- a/generates_dependency_documents.py +++ b/generates_dependency_documents.py @@ -195,10 +195,10 @@ def main(): 'the example above)\n\t\t\t3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and ' '"9606" in the example above).\n\n\tAn example line from the resource_info.txt file is shown below:\n\t\t' 'chemical-gene|;MESH_;|class-class|;MESH_;|class-class|RO_0002434|#|t|1;4|0:./resources/data_maps/' - 'MESH_CHEBI_MAP.txt|None|7;==;9606\n\n(2) ontology_source_info.txt: This document contains a ","-delimited ' - 'line for each ontology source used, for example:\n\t"chemical, http://purl.obolibrary.org/obo/chebi.owl"' - '\n\t"gene, http://purl.obolibrary.org/obo/so.owl"\n\n(3) edge_source_info.txt: This document contains a ' - '","-delimited line for each edge data source, for example:\n\t"chemical-gene, ' + 'MESH_CHEBI_MAP.txt|None|7;==;9606\n\n(2) ontology_source_info.txt: This document contains a "|"-delimited ' + 'line for each ontology source used, for example:\n\t"chemical|http://purl.obolibrary.org/obo/chebi.owl"' + '\n\t"gene|http://purl.obolibrary.org/obo/so.owl"\n\n(3) edge_source_info.txt: This document contains a ' + '"|"-delimited line for each edge data source, for example:\n\t"chemical-gene|' 'http://ctdbase.org/reports/CTD_chem_gene_ixns.tsv.gz"\n\nIf you would like more information on the ' 'dependency documents need to run PheKnowLator, please visit the following Wiki page:\n' 'https://github.com/callahantiff/PheKnowLator/wiki/Dependencies.') @@ -216,10 +216,10 @@ def main(): edge_maker.writes_out_document(edge_data[0], '|', 'resource_info.txt') # write out ontology data - edge_maker.writes_out_document(edge_data[1], ', ', 'ontology_source_list.txt') + edge_maker.writes_out_document(edge_data[1], '|', 'ontology_source_list.txt') # write out edge data - edge_maker.writes_out_document(edge_data[2], ', ', 'edge_source_list.txt') + edge_maker.writes_out_document(edge_data[2], '|', 'edge_source_list.txt') if __name__ == '__main__': From ba1ffa33d3fbe6f1d072caf9b5db076a4395dde9 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 14:02:37 -0700 Subject: [PATCH 051/112] updating testing data --- tests/data/edge_source_list.txt | 8 ++++++- tests/data/ontology_source_list.txt | 8 ++++++- tests/data/resource_info.txt | 35 +++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/tests/data/edge_source_list.txt b/tests/data/edge_source_list.txt index 19407f1e..e0265fa4 100644 --- a/tests/data/edge_source_list.txt +++ b/tests/data/edge_source_list.txt @@ -1 +1,7 @@ -chemical-disease, http://ctdbase.org/reports/CTD_chemicals_diseases.tsv.gz \ No newline at end of file +##################################################################################################################################################### +#### edge_source_info.txt (last updated: December 27, 2021) +### Each column is separated by a pipe (i.e., "|") and includes the following: +# EdgeType: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and ontology_source_list.txt files. +# URL: A string containing a URL to the primary data source for the edge. +##################################################################################################################################################### +chemical-disease|http://ctdbase.org/reports/CTD_chemicals_diseases.tsv.gz \ No newline at end of file diff --git a/tests/data/ontology_source_list.txt b/tests/data/ontology_source_list.txt index a354f197..ccd98081 100644 --- a/tests/data/ontology_source_list.txt +++ b/tests/data/ontology_source_list.txt @@ -1 +1,7 @@ -phenotype, http://purl.obolibrary.org/obo/hp.owl \ No newline at end of file +################################################################################################################################################### +#### ontology_source_info.txt (last updated: December 27, 2021) +### Each column is separated by a pipe (i.e., "|") and includes the following: +# Ontology: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and edge_source_list.txt files. +# URL: A string containing a URL to the ontology file. +#################################################################################################################################################### +phenotype|http://purl.obolibrary.org/obo/hp.owl \ No newline at end of file diff --git a/tests/data/resource_info.txt b/tests/data/resource_info.txt index 6d2ad1cd..577badaf 100644 --- a/tests/data/resource_info.txt +++ b/tests/data/resource_info.txt @@ -1,2 +1,33 @@ -chemical-disease|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:MESH_CHEBI_MAP.txt;1:DISEASE_DOID_MAP.txt|5;!=;''|None -gene-disease|;;|entity-class|RO_0003302|http://purl.uniprot.org/geneid/|http://purl.obolibrary.org/obo/|t|0;4|1:DISEASE_DOID_MAP.txt|10;>=;0.70|None \ No newline at end of file +###################################################################################################################################################################################### +#### resource_info.txt (last updated: December 27, 2021) +### Each column is separated by a pipe (i.e., "|") and includes the following: +# EdgeType: A string label for an edge (node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files. +# IdentifierPrefixInformation: Three ";"-separated items used to update a prefix-identifier pair (e.g., :;GO_;GO_). The first item contains the character that separates +# existing prefixes and identifiers (e.g. ":" in GO:1283834). The second item contains the current prefix and the third item contains the +# new prefix (i.e. 'GO_' and 'GO_'). If the existing prefix is correct, type ";;". if there is no prefix in the current data, leave the +# item empty and specify the new prefix for the node in the corresponding item location. +# NodeDataTypes: A label of "class" or "entity" for each node in an edge separated by "-" (e.g., "class-class"). The "class" label represents nodes from +# ontologies and "entity" represents nodes from other data sources. +# Relation: A Relation Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056). +# Delimiter: A character used to split rows from an input data source into columns (e.g., "t" for tab-delimited data or "," for comma-delimited data). +# ColumnIndexes: Two-column indexes separated by ";" (e.g., "0;4" for the first and third columns in the input data source). +# IdentifierMaps: A string of mapping information for each node in an edge. For example, the string "2:mapping_file_1.txt;4:mapping_file_2.txt" means that +# the first node requires data contained in the 2nd column of the "mapping_file_1.txt" and the second node requires data from the 4th column +# in the "mapping_file_2.txt" file. +# EvidenceCriteria: Evidence criteria that can be used to filter an input data source (e.g., scores above a certain cut-off). An evidence set is composed of 3 +# pieces of ";"-separated information. Multiple evidence sets can be passed, where each set is separated by "::". Consider the following +# example: "4;!=;IEA::8;<;0.0001": +# 1. The index of the column to apply the evidence criteria to (e.g., "4" and "8" in the example above) +# 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" +# in the example above) +# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and "0.0001" in the example above) +# FilterCriteria: Filtering criteria that can be used to filter an input data source (e.g., human proteins). An evidence set is composed of 3 pieces of ";"- +# separated information. Multiple filtering sets can be passed, where each set is separated by "::". Consider the following example: +# "5;==;P::7;==;9606"): +# 1. The index of the column to apply the evidence criteria to (e.g., "5" and "7" in the example above) +# 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" +# in the example above) +# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above) +###################################################################################################################################################################################### +chemical-disease|:;MESH_;|class-class|RO_0002606|t|1;4|0:MESH_CHEBI_MAP.txt;1:DISEASE_DOID_MAP.txt|5;!=;''|None +gene-disease|;;|entity-class|RO_0003302|t|0;4|1:DISEASE_DOID_MAP.txt|10;>=;0.70|None \ No newline at end of file From e947ab6ea81bf194b7e17adec871aa4dbd2e38c6 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 14:03:14 -0700 Subject: [PATCH 052/112] fixing language --- resources/resource_info.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index da7b6913..bf641d88 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -15,19 +15,19 @@ # the first node requires data contained in the 2nd column of the "mapping_file_1.txt" and the second node requires data from the 4th column # in the "mapping_file_2.txt" file. # EvidenceCriteria: Evidence criteria that can be used to filter an input data source (e.g., scores above a certain cut-off). An evidence set is composed of 3 -# pieces of ";"-separated information (e.g., "4;!=;IEA::8;<;0.0001"): +# pieces of ";"-separated information. Multiple evidence sets can be passed, where each set is separated by "::". Consider the following +# example: "4;!=;IEA::8;<;0.0001": # 1. The index of the column to apply the evidence criteria to (e.g., "4" and "8" in the example above) # 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" # in the example above) # 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and "0.0001" in the example above) -# Multiple evidence sets can be passed as demonstrated by the example above, where each set is separated by "::". # FilterCriteria: Filtering criteria that can be used to filter an input data source (e.g., human proteins). An evidence set is composed of 3 pieces of ";"- -# separated information (e.g., "5;==;P::7;==;9606"): +# separated information. Multiple filtering sets can be passed, where each set is separated by "::". Consider the following example: +# "5;==;P::7;==;9606"): # 1. The index of the column to apply the evidence criteria to (e.g., "5" and "7" in the example above) # 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" # in the example above) -# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above) -# Multiple filtering sets can be passed as demonstrated by the example above, where each set is separated by "::". +# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above) ###################################################################################################################################################################################### chemical-disease|:;MESH_;|class-class|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None chemical-gene|;MESH_;NCBIGene_|class-entity|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); From 8b4e8f81e2b27f0b00c30ff9d07da6b102c22862 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 14:03:31 -0700 Subject: [PATCH 053/112] adding header and making "|"-delimited --- resources/edge_source_list.txt | 74 ++++++++++++++++-------------- resources/ontology_source_list.txt | 28 ++++++----- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/resources/edge_source_list.txt b/resources/edge_source_list.txt index bfbbcbad..baaf89cb 100644 --- a/resources/edge_source_list.txt +++ b/resources/edge_source_list.txt @@ -1,34 +1,40 @@ -chemical-disease, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chemicals_diseases.tsv -chemical-gene, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_gene_ixns.tsv -chemical-gobp, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_go_enriched.tsv -chemical-gocc, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_go_enriched.tsv -chemical-gomf, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_go_enriched.tsv -chemical-pathway, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/ChEBI2Reactome_All_Levels.txt -chemical-phenotype, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chemicals_diseases.tsv -chemical-protein, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_gene_ixns.tsv -chemical-rna, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_gene_ixns.tsv -disease-phenotype, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/phenotype.hpoa -gene-disease, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/curated_gene_disease_associations.tsv -gene-gene, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt -gene-pathway, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_genes_pathways.tsv -gene-phenotype, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/curated_gene_disease_associations.tsv -gene-protein, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt -gene-rna, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt -gobp-pathway, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/gene_association.reactome -pathway-gocc, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/gene_association.reactome -pathway-gomf, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/gene_association.reactome -protein-anatomy, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt -protein-catalyst, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_CATALYST.txt -protein-cell, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt -protein-cofactor, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_COFACTOR.txt -protein-gobp, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/goa_human.gaf -protein-gocc, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/goa_human.gaf -protein-gomf, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/goa_human.gaf -protein-pathway, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/UniProt2Reactome_All_Levels.txt -protein-protein, https://storage.googleapis.com/pheknowlator/current_build/data/original_data/9606.protein.links.v11.0.txt -rna-anatomy, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt -rna-cell, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt -rna-protein, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENSEMBL_TRANSCRIPT_PROTEIN_ONTOLOGY_MAP.txt -variant-disease, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt -variant-gene, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt -variant-phenotype, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt \ No newline at end of file +##################################################################################################################################################### +#### edge_source_info.txt (last updated: December 27, 2021) +### Each column is separated by a pipe (i.e., "|") and includes the following: +# EdgeType: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and ontology_source_list.txt files. +# URL: A string containing a URL to the primary data source for the edge. +##################################################################################################################################################### +chemical-disease|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chemicals_diseases.tsv +chemical-gene|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_gene_ixns.tsv +chemical-gobp|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_go_enriched.tsv +chemical-gocc|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_go_enriched.tsv +chemical-gomf|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_go_enriched.tsv +chemical-pathway|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/ChEBI2Reactome_All_Levels.txt +chemical-phenotype|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chemicals_diseases.tsv +chemical-protein|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_gene_ixns.tsv +chemical-rna|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_gene_ixns.tsv +disease-phenotype|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/phenotype.hpoa +gene-disease|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/curated_gene_disease_associations.tsv +gene-gene|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt +gene-pathway|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_genes_pathways.tsv +gene-phenotype|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/curated_gene_disease_associations.tsv +gene-protein|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt +gene-rna|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt +gobp-pathway|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/gene_association.reactome +pathway-gocc|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/gene_association.reactome +pathway-gomf|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/gene_association.reactome +protein-anatomy|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt +protein-catalyst|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_CATALYST.txt +protein-cell|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt +protein-cofactor|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_COFACTOR.txt +protein-gobp|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/goa_human.gaf +protein-gocc|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/goa_human.gaf +protein-gomf|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/goa_human.gaf +protein-pathway|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/UniProt2Reactome_All_Levels.txt +protein-protein|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/9606.protein.links.v11.0.txt +rna-anatomy|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt +rna-cell|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt +rna-protein|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENSEMBL_TRANSCRIPT_PROTEIN_ONTOLOGY_MAP.txt +variant-disease|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt +variant-gene|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt +variant-phenotype|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt \ No newline at end of file diff --git a/resources/ontology_source_list.txt b/resources/ontology_source_list.txt index d901f1eb..58385362 100644 --- a/resources/ontology_source_list.txt +++ b/resources/ontology_source_list.txt @@ -1,11 +1,17 @@ -phenotype, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/hp_with_imports.owl -go, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/go_with_imports.owl -disease, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/mondo_with_imports.owl -vaccine, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/vo_with_imports.owl -chemical, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/chebi_with_imports.owl -anatomy, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ext_with_imports.owl -cell, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/clo_with_imports.owl -protein, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/pr_with_imports.owl -genomic, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/so_with_imports.owl -pathway, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/pw_with_imports.owl -relation, https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ro_with_imports.owl \ No newline at end of file +################################################################################################################################################### +#### ontology_source_info.txt (last updated: December 27, 2021) +### Each column is separated by a pipe (i.e., "|") and includes the following: +# Ontology: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and edge_source_list.txt files. +# URL: A string containing a URL to the ontology file. +#################################################################################################################################################### +phenotype|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/hp_with_imports.owl +go|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/go_with_imports.owl +disease|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/mondo_with_imports.owl +vaccine|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/vo_with_imports.owl +chemical|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/chebi_with_imports.owl +anatomy|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ext_with_imports.owl +cell|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/clo_with_imports.owl +protein|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/pr_with_imports.owl +genomic|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/so_with_imports.owl +pathway|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/pw_with_imports.owl +relation|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ro_with_imports.owl \ No newline at end of file From adb82df29d85f40e18abc5384d77076ada3859b7 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 14:09:46 -0700 Subject: [PATCH 054/112] fixed typos and updated delimiter --- pkt_kg/downloads.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pkt_kg/downloads.py b/pkt_kg/downloads.py index e82b6dd2..e1d4dacf 100644 --- a/pkt_kg/downloads.py +++ b/pkt_kg/downloads.py @@ -136,9 +136,7 @@ def extracts_edge_metadata(edge) -> Tuple[str, str, str]: """ mapping = ['{} ({})'.format(edge.split('|')[0].split('-')[int(x.split(':')[0])], ''.join(x.split(':')[1])) - if x != 'None' - else 'None' - for x in edge.split('|')[-3].strip('\n').split(';')] + if x != 'None' else 'None' for x in edge.split('|')[-3].strip('\n').split(';')] filtering = ['None' if x == 'None' else 'data[{}] {}'.format(x.split(';')[0], ' '.join(x.split(';')[1:])) if ('in' in x.split(';')[1] and x != 'None') @@ -238,7 +236,7 @@ def parses_resource_file(self) -> None: Returns: source_list: A dictionary, where the key is the type of data and the value is the file path or url. See - example below: {'chemical-gomf', 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz', + example below: {'chemical-gomf': 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz', 'phenotype': 'http://purl.obolibrary.org/obo/hp.owl'} Raises: @@ -251,8 +249,8 @@ def parses_resource_file(self) -> None: raise TypeError('ERROR: ' + log_str) else: with open(self.data_path, 'r') as file_name: - self.source_list = {row.strip().split(',')[0]: row.strip().split(',')[1].strip() - for row in file_name.read().splitlines()} + self.source_list = {row.strip().split('|')[0]: row.strip().split('|')[1].strip() + for row in file_name.read().splitlines() if not row.startswith('#')} return None @@ -316,7 +314,7 @@ def parses_resource_file(self) -> None: Returns: source_list: A dictionary, where the key is the type of data and the value is the file path or url. See - example below: {'chemical-gomf', 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz', + example below: {'chemical-gomf': 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz', 'phenotype': 'http://purl.obolibrary.org/obo/hp.owl'} Raises: @@ -328,8 +326,8 @@ def parses_resource_file(self) -> None: raise TypeError('ERROR: ' + log_str) else: with open(self.data_path, 'r') as file_name: - self.source_list = {row.strip().split(',')[0]: row.strip().split(',')[1].strip() - for row in file_name.read().splitlines()} + self.source_list = {row.strip().split('|')[0]: row.strip().split('|')[1].strip() + for row in file_name.read().splitlines() if not row.startswith('#')} return None From ebbe8892f5316f69e344a6324fb0b2814645e7da Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 15:04:34 -0700 Subject: [PATCH 055/112] improving def --- generates_dependency_documents.py | 46 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/generates_dependency_documents.py b/generates_dependency_documents.py index a1aa2783..290ab5b1 100644 --- a/generates_dependency_documents.py +++ b/generates_dependency_documents.py @@ -164,30 +164,28 @@ def main(): 'documents is shown below:\n\n(1) resource_info.txt: This document represents each edge type as a single ' '"|" delimited string and contains a total of 9 items:\n\t(1) EdgeType: A string label for an edge ' '(node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files' - '\n\t(2) IdentifierPrefixInformation: Three ";"-separated items used to update a prefix-identifier pair ' - '(e.g., :;GO_;GO_). The first item contains the character that separates existing\n\t\tprefixes and ' - 'identifiers (e.g. ":" in GO:1283834). The second item contains the current prefix and the third item ' - 'contains the new prefix (i.e. "GO_" and "GO_"). If the existing\n\t\tprefix is correct, type ";;". if there ' - 'is no prefix in the current data, leave the item empty and specify the new prefix for the node in the ' - 'corresponding item location;\n\t(3) NodeDataTypes: A label of "class" or "entity" for each node in an edge ' - 'separated by "-" (e.g., "class-class"). The "class" label\n\t\trepresents nodes from ontologies and ' - '"entity" represents nodes from other data sources;\n\t(4) Relation: A Relation Ontology (' - 'http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056)\n\t(5) Delimiter: A character used to ' - 'split rows from an input data source into columns (e.g., "t" for tab-delimited data or "," for ' - 'comma-delimited data);\n\t(6) ColumnIndexes: Two-column indexes separated by ";" (e.g., "0;4" for the first ' - 'and third columns in the input data source);\n\t(7) IdentifierMaps: A string of mapping information for ' - 'each node in an edge. For example, the string "2:mapping_file_1.txt;4:mapping_file_2.txt" means that the ' - 'first node require\n\t\tdata contained in the 2nd column of the "mapping_file_1.txt" and the second node ' - 'requires data from the 4th column in the "mapping_file_2.txt" file;\n\t(8) EvidenceCriteria: Evidence ' - 'criteria that can be used to filter an input data source (e.g., scores above a certain cut-off). An ' - 'evidence set is composed of 3 pieces of ";"\n\t\t-separated information. Multiple filtering sets can be ' - 'passed, where each set is separated by "::". Consider the following example: ' - '"4;!=;IEA::8;<;0.0001"):\n\t\t\t1. The index of the column to apply the evidence criteria to ' - '(e.g., "4" and "8" in the example above)\n\t\t\t2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", ' - '"in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" in the example above).' - '\n\t\t\t3.The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and "0.0001" in the ' - 'example above);\n\t(9) Filtering criteria that can be used to filter an input data source (e.g., ' - 'human proteins). An evidence set is composed of 3 pieces of ";"-separated information.\n\t\tMultiple ' + '\n\t(2) IdentifierPrefixInformation: A ";"-separated string used to update a prefix-identifier pair ' + '(e.g., GO;GO). The first and second items contain BioRegistry prefixes. If one of the\n\t\texisting ' + 'prefixes is correct leave its spot empty and if both are correct, type ";". All prefixes should be the ' + 'preferred prefix from the BioRegistry\n\t\t(https://bioregistry.io/registry/);\n\t(3) NodeDataTypes: A ' + 'label of "class" or "entity" for each node in an edge separated by "-" (e.g., "class-class"). The "class" ' + 'label\n\t\trepresents nodes from ontologies and "entity" represents nodes from other data sources;\n\t(4) ' + 'Relation: A Relation Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056)\n\t(5) ' + 'Delimiter: A character used to split rows from an input data source into columns (e.g., "t" for ' + 'tab-delimited data or "," for comma-delimited data);\n\t(6) ColumnIndexes: Two-column indexes separated by ' + '";" (e.g., "0;4" for the first and third columns in the input data source);\n\t(7) IdentifierMaps: A string ' + 'of mapping information for each node in an edge. For example, the string "2:mapping_file_1.txt;' + '4:mapping_file_2.txt" means that the first node require\n\t\tdata contained in the 2nd column of the ' + '"mapping_file_1.txt" and the second node requires data from the 4th column in the "mapping_file_2.txt" ' + 'file;\n\t(8) EvidenceCriteria: Evidence criteria that can be used to filter an input data source (e.g., ' + 'scores above a certain cut-off). An evidence set is composed of 3 pieces of ";"\n\t\t-separated ' + 'information. Multiple filtering sets can be passed, where each set is separated by "::". Consider the ' + 'following example: "4;!=;IEA::8;<;0.0001"):\n\t\t\t1. The index of the column to apply the evidence ' + 'criteria to (e.g., "4" and "8" in the example above)\n\t\t\t2. The operator (i.e., "==", "!=", "<", ' + '">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" in the ' + 'example above).\n\t\t\t3.The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and ' + '"0.0001" in the example above);\n\t(9) Filtering criteria that can be used to filter an input data source ' + '(e.g., human proteins). An evidence set is composed of 3 pieces of ";"-separated information.\n\t\tMultiple ' 'filtering sets can be passed as demonstrated by the example above, where each set is separated by "::". ' 'Consider the following example: "5;==;P::7;==;9606"):\n\t\t\t1. The index of the column to apply the ' 'evidence criteria to (e.g., "5" and "7" in the example above)\n\t\t\t2. The operator (i.e., "==", "!=", ' From 8dec76f2984f9909f1df7232e418d7ec2b1b65a4 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 15:04:48 -0700 Subject: [PATCH 056/112] improving column def --- resources/resource_info.txt | 75 ++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index bf641d88..f2368774 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -2,10 +2,9 @@ #### resource_info.txt (last updated: December 27, 2021) ### Each column is separated by a pipe (i.e., "|") and includes the following: # EdgeType: A string label for an edge (node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files. -# IdentifierPrefixInformation: Three ";"-separated items used to update a prefix-identifier pair (e.g., :;GO_;GO_). The first item contains the character that separates -# existing prefixes and identifiers (e.g. ":" in GO:1283834). The second item contains the current prefix and the third item contains the -# new prefix (i.e. 'GO_' and 'GO_'). If the existing prefix is correct, type ";;". if there is no prefix in the current data, leave the -# item empty and specify the new prefix for the node in the corresponding item location. +# IdentifierPrefixInformation: A ";"-separated string used to update a prefix-identifier pair (e.g., GO;GO). The first and second items contain BioRegistry prefixes +# that should be used for the subject and object nodes of an edge, respectively. If one of the existing prefixes is correct leave its spot +# empty and if both are correct, type ";".All prefixes should be the preferred prefix from the BioRegistry (https://bioregistry.io/registry/). # NodeDataTypes: A label of "class" or "entity" for each node in an edge separated by "-" (e.g., "class-class"). The "class" label represents nodes from # ontologies and "entity" represents nodes from other data sources. # Relation: A Relation Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056). @@ -29,37 +28,37 @@ # in the example above) # 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above) ###################################################################################################################################################################################### -chemical-disease|:;MESH_;|class-class|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None -chemical-gene|;MESH_;NCBIGene_|class-entity|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); -chemical-gobp|:;MESH_;GO_|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Biological Process -chemical-gocc|:;MESH_;GO_|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Cellular Component -chemical-gomf|:;MESH_;GO_|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Molecular Function -chemical-pathway|;CHEBI_;|class-entity|RO_0000056|t|0;1|None|None|5;==;Homo sapiens -chemical-phenotype|:;MESH_;|class-class|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None -chemical-protein|;MESH_;|class-class|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); -chemical-rna|;MESH_;|class-entity|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); -disease-phenotype|:;;HP_|class-class|RO_0002200|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|2;!=;NOT -gene-disease|;NCBIGene;|entity-class|RO_0003302|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|6;!=;group -gene-gene|;NCBIGene;NCBIGene|entity-entity|RO_0002435|t|0;1|0:./resources/processed_data/OTHER_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None -gene-pathway|:;NCBIGene;|entity-entity|RO_0000056|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); -gene-phenotype|;NCBIGene;|entity-class|RO_0003302|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|6;!=;group -gene-protein|;NCBIGene;|entity-class|RO_0002205|t|0;1|None|None|4;==;protein-coding -gene-rna|;NCBIGene;|entity-entity|RO_0002511|t|0;1|None|None|None -gobp-pathway|:;GO_;|class-entity|RO_0009501|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -pathway-gocc|:;;GO_|entity-class|RO_0002180|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -pathway-gomf|:;;GO_|entity-class|RO_0000085|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -protein-anatomy|;;|class-class|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;anatomy -protein-catalyst|;;|class-class|RO_0002436|t|0;1|None|None|None|None -protein-cell|;;|class-class|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;cell line -protein-cofactor|;;|class-class|RO_0002436|t|0;1|None|None|None|None -protein-gobp|:;;GO_|class-class|RO_0000056|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein -protein-gocc|:;;GO_|class-class|RO_0001025|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein -protein-gomf|:;;GO_|class-class|RO_0000085|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein -protein-pathway|;;|class-entity|RO_0000056|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens -protein-protein|9606.;;|class-class|RO_0002436|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|None|None -rna-anatomy|;;|entity-class|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy -rna-cell|;;|entity-class|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line. -rna-protein|;;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding. -variant-disease|:;rs;|entity-class|RO_0003302|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc. -variant-gene|;rs;NCBIGene|entity-entity|RO_0002566|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc. -variant-phenotype|:;rs;|entity-class|RO_0003302|t|9;12|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc \ No newline at end of file +chemical-disease|MESH;|class-class|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None +chemical-gene|MESH;NCBIGene|class-entity|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); +chemical-gobp|MESH;GO|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Biological Process +chemical-gocc|;MESH;GO|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Cellular Component +chemical-gomf|MESH;GO|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Molecular Function +chemical-pathway|CHEBI;|class-entity|RO_0000056|t|0;1|None|None|5;==;Homo sapiens +chemical-phenotype|MESH;|class-class|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None +chemical-protein|MESH;|class-class|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); +chemical-rna|MESH;|class-entity|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); +disease-phenotype|;HP|class-class|RO_0002200|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|2;!=;NOT +gene-disease|NCBIGene;|entity-class|RO_0003302|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|6;!=;group +gene-gene|NCBIGene;NCBIGene|entity-entity|RO_0002435|t|0;1|0:./resources/processed_data/OTHER_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None +gene-pathway|NCBIGene;|entity-entity|RO_0000056|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); +gene-phenotype|NCBIGene;|entity-class|RO_0003302|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|6;!=;group +gene-protein|NCBIGene;|entity-class|RO_0002205|t|0;1|None|None|4;==;protein-coding +gene-rna|NCBIGene;|entity-entity|RO_0002511|t|0;1|None|None|None +gobp-pathway|GO;|class-entity|RO_0009501|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +pathway-gocc|;GO|entity-class|RO_0002180|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +pathway-gomf|;GO|entity-class|RO_0000085|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +protein-anatomy|;|class-class|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;anatomy +protein-catalyst|;|class-class|RO_0002436|t|0;1|None|None|None|None +protein-cell|;|class-class|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;cell line +protein-cofactor|;|class-class|RO_0002436|t|0;1|None|None|None|None +protein-gobp|;GO|class-class|RO_0000056|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-gocc|;GO|class-class|RO_0001025|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-gomf|;GO|class-class|RO_0000085|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-pathway|;|class-entity|RO_0000056|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens +protein-protein|;|class-class|RO_0002436|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|None|None +rna-anatomy|;|entity-class|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy +rna-cell|;|entity-class|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line. +rna-protein|;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding. +variant-disease|rs;|entity-class|RO_0003302|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc. +variant-gene|rs;NCBIGene|entity-entity|RO_0002566|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc. +variant-phenotype|rs;|entity-class|RO_0003302|t|9;12|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc \ No newline at end of file From 90df2ecb5ca3749a896021106b7eab323dc0a850 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 15:07:50 -0700 Subject: [PATCH 057/112] adding bioregistry function --- pkt_kg/utils/__init__.py | 6 +++--- pkt_kg/utils/data_utils.py | 27 ++++++++++++++++++++++++++ tests/test_data_utils_miscellaneous.py | 23 ++++++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/pkt_kg/utils/__init__.py b/pkt_kg/utils/__init__.py index 75ddb510..9aa9d0b6 100644 --- a/pkt_kg/utils/__init__.py +++ b/pkt_kg/utils/__init__.py @@ -8,9 +8,9 @@ __all__ = ['url_download', 'ftp_url_download', 'gzipped_ftp_url_download', 'zipped_url_download', 'gzipped_url_download', 'data_downloader', 'explodes_data', 'chunks', 'metadata_dictionary_mapper', - 'metadata_api_mapper', 'genomic_id_mapper', 'outputs_dictionary_data', 'gets_ontology_statistics', - 'gets_ontology_classes', 'gets_deprecated_ontology_classes', 'gets_object_properties', - 'gets_ontology_class_dbxrefs', 'gets_ontology_class_synonyms', 'merges_ontologies', + 'metadata_api_mapper', 'genomic_id_mapper', 'outputs_dictionary_data', 'obtains_entity_url', + 'gets_ontology_statistics', 'gets_ontology_classes', 'gets_deprecated_ontology_classes', + 'gets_object_properties', 'gets_ontology_class_dbxrefs', 'gets_ontology_class_synonyms', 'merges_ontologies', 'ontology_file_formatter', 'adds_edges_to_graph', 'remove_edges_from_graph', 'gets_entity_ancestors', 'connected_components', 'removes_self_loops', 'derives_graph_statistics', 'splits_knowledge_graph', 'adds_namespace_to_bnodes', 'removes_namespace_from_bnodes', 'updates_pkt_namespace_identifiers', diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index 82ebcd92..ad14fdaa 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -23,6 +23,7 @@ * deduplicates_file * merges_files * sublist_creator +* obtains_entity_url Outputs data * outputs_dictionary_data @@ -43,6 +44,7 @@ from contextlib import closing from io import BytesIO +from json.decoder import JSONDecodeError from reactome2py import content # type: ignore from tqdm import tqdm # type: ignore from typing import Dict, Generator, List, Optional, Union @@ -478,3 +480,28 @@ def sublist_creator(actors: Union[Dict, List], chunk_size: int) -> List: else: updated_lists = lists return updated_lists + + +def obtains_entity_url(prefix: str, identifier: Union[int, str]) -> str: + """Function takes a prefix and identifier for an entity, looks it up in the BioRegistry API and returns a + resolvable URL. Information on the BioRegistry can be found here: https://bioregistry.io/. + + Args: + prefix: A string containing the prefix or name of a resources (e.g., "chebi"). + identifier: A string or integer containing an entity identifier (e.g., "138488"). + + Returns: + entity_url: A string containing a valid BioRegistry URL (e.g., ). + + Raises: + ValueError: If a JSONDecodeError is thrown, a ValueError is raised to alert the user that a bad identifier or + prefix was provided. + """ + + try: + res = requests.get('https://bioregistry.io/api/reference/' + prefix.lower() + ':' + str(identifier)).json() + except JSONDecodeError as e: + raise ValueError('Error: Invalid prefix or identifier provided. Please check your input and try again.') + entity_url = res['providers']['bioregistry'] + + return entity_url diff --git a/tests/test_data_utils_miscellaneous.py b/tests/test_data_utils_miscellaneous.py index 32182f31..f2dece75 100644 --- a/tests/test_data_utils_miscellaneous.py +++ b/tests/test_data_utils_miscellaneous.py @@ -154,6 +154,29 @@ def tests_sublist_creator_list(self): return None + def tests_obtains_entity_url_good(self): + """Tests the obtains_entity_url method when a valid prefix and identifier are passed.""" + + # set-up input + prefix = 'chebi'; identifier = '138488' + + # test function + entity_uri = obtains_entity_url(prefix, identifier) + self.assertEqual(entity_uri, 'https://bioregistry.io/chebi:138488') + + return None + + def tests_obtains_entity_url_bad(self): + """Tests the obtains_entity_url method when an invalid identifier is passed.""" + + # set-up input + prefix = 'chebi'; identifier = 't' + + # test function + self.assertRaises(ValueError, obtains_entity_url, prefix, identifier) + + return None + def tearDown(self): # remove temp directory From a424c2185aed52d5257d2cf092e008edce6a0588 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 16:02:36 -0700 Subject: [PATCH 058/112] place holder for ontology dbxref retrieval --- pkt_kg/metadata.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkt_kg/metadata.py b/pkt_kg/metadata.py index 0ec8dc98..b4b98a49 100644 --- a/pkt_kg/metadata.py +++ b/pkt_kg/metadata.py @@ -132,11 +132,15 @@ def extract_metadata(self, graph: Graph) -> None: descriptions = [x for x in graph.triples((i, obo.IAO_0000115, None)) if '@' not in n3(x[2]) or '@en' in n3(x[2])] synonyms = [x for x in graph.triples((i, None, None)) if 'synonym' in str(x[1]).lower()] + dbxrefs = [x for x in graph.triples((i, None, None)) + if 'hasdbxref' in str(x[1]).lower() or 'exactmatch' in str(x[1]).lower()] if len(labels) != 0: temp_dict[str(i)] = { 'Label': str(labels[0][2]) if len(labels) > 0 else None, 'Description': str(descriptions[0][2]) if len(descriptions) > 0 else None, - 'Synonym': '|'.join([str(c[2]) for c in synonyms]) if len(synonyms) > 0 else None + 'Synonym': '|'.join([str(c[2]) for c in synonyms]) if len(synonyms) > 0 else None, + 'DbXref': '|'.join(['{}:{}'.format(str(c[1]), str(c[2])) for c in dbxrefs]) + if len(dbxrefs) > 0 else None } self.node_dict[key] = {**self.node_dict[key], **temp_dict} From ef36bce2e126dff6ffbc42035c5ec23537594161 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 16:56:46 -0700 Subject: [PATCH 059/112] adding pyyaml to requirements --- builds/build_requirements.txt | 1 + notebooks/requirements.txt | 1 + setup.py | 1 + 3 files changed, 3 insertions(+) diff --git a/builds/build_requirements.txt b/builds/build_requirements.txt index 561a68fb..ef1f0799 100644 --- a/builds/build_requirements.txt +++ b/builds/build_requirements.txt @@ -12,6 +12,7 @@ oauth2client~=4.1.3 Owlready2==0.25 pandas==1.0.5 python-json-logger==2.0.1 +pyyaml ray~=1.1.0 rdflib==4.2.2 reactome2py==0.0.8 diff --git a/notebooks/requirements.txt b/notebooks/requirements.txt index bb0e177d..251df442 100644 --- a/notebooks/requirements.txt +++ b/notebooks/requirements.txt @@ -6,6 +6,7 @@ openpyxl>=3.0.3 pandas>=1.0.5 psutil>=5.6.3 python-json-logger>=2.0.1 +pyyaml ray>=1.1.0 rdflib>=4.2.2 reactome2py>=0.0.8 diff --git a/setup.py b/setup.py index 3a5cfce1..7d5b6b60 100644 --- a/setup.py +++ b/setup.py @@ -79,6 +79,7 @@ def find_version(*file_paths): 'pandas>=1.0.5', 'psutil', 'python-json-logger', + 'pyyaml', 'ray', 'rdflib', 'reactome2py', From 58adf49520879ee0b4df351d5c878fd14da891ec Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 27 Dec 2021 18:03:16 -0700 Subject: [PATCH 060/112] adding biolink functionality --- pkt_kg/utils/__init__.py | 13 +++--- pkt_kg/utils/data_utils.py | 56 +++++++++++++++++++++++++- tests/test_data_utils_miscellaneous.py | 49 ++++++++++++++++++++++ 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/pkt_kg/utils/__init__.py b/pkt_kg/utils/__init__.py index 9aa9d0b6..8f6e690a 100644 --- a/pkt_kg/utils/__init__.py +++ b/pkt_kg/utils/__init__.py @@ -9,10 +9,11 @@ __all__ = ['url_download', 'ftp_url_download', 'gzipped_ftp_url_download', 'zipped_url_download', 'gzipped_url_download', 'data_downloader', 'explodes_data', 'chunks', 'metadata_dictionary_mapper', 'metadata_api_mapper', 'genomic_id_mapper', 'outputs_dictionary_data', 'obtains_entity_url', - 'gets_ontology_statistics', 'gets_ontology_classes', 'gets_deprecated_ontology_classes', - 'gets_object_properties', 'gets_ontology_class_dbxrefs', 'gets_ontology_class_synonyms', 'merges_ontologies', - 'ontology_file_formatter', 'adds_edges_to_graph', 'remove_edges_from_graph', 'gets_entity_ancestors', - 'connected_components', 'removes_self_loops', 'derives_graph_statistics', 'splits_knowledge_graph', - 'adds_namespace_to_bnodes', 'removes_namespace_from_bnodes', 'updates_pkt_namespace_identifiers', - 'finds_node_type', 'updates_graph_namespace', 'maps_ids_to_integers', 'n3', 'appends_to_existing_file', + 'gets_biolink_information', 'gets_ontology_statistics', 'gets_ontology_classes', + 'gets_deprecated_ontology_classes', 'gets_object_properties', 'gets_ontology_class_dbxrefs', + 'gets_ontology_class_synonyms', 'merges_ontologies', 'ontology_file_formatter', 'adds_edges_to_graph', + 'remove_edges_from_graph', 'gets_entity_ancestors', 'connected_components', 'removes_self_loops', + 'derives_graph_statistics', 'splits_knowledge_graph', 'adds_namespace_to_bnodes', + 'removes_namespace_from_bnodes', 'updates_pkt_namespace_identifiers', 'finds_node_type', + 'updates_graph_namespace', 'maps_ids_to_integers', 'n3', 'appends_to_existing_file', 'deduplicates_file', 'merges_files', 'convert_to_networkx', 'sublist_creator', 'gets_ontology_definitions'] diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index ad14fdaa..9535e355 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -24,6 +24,7 @@ * merges_files * sublist_creator * obtains_entity_url +* gets_biolink_information Outputs data * outputs_dictionary_data @@ -41,10 +42,12 @@ import requests import shutil import urllib3 # type: ignore +import yaml from contextlib import closing from io import BytesIO from json.decoder import JSONDecodeError +from rdflib import Graph # type: ignore from reactome2py import content # type: ignore from tqdm import tqdm # type: ignore from typing import Dict, Generator, List, Optional, Union @@ -500,8 +503,59 @@ def obtains_entity_url(prefix: str, identifier: Union[int, str]) -> str: try: res = requests.get('https://bioregistry.io/api/reference/' + prefix.lower() + ':' + str(identifier)).json() - except JSONDecodeError as e: + except JSONDecodeError: raise ValueError('Error: Invalid prefix or identifier provided. Please check your input and try again.') entity_url = res['providers']['bioregistry'] return entity_url + + +def gets_biolink_information(entity: str, entity_label: Optional[str] = None, biolink_loc='./resources/') -> str: + """Function takes an entity CURIE and label and returns its BioLink Model type. First, the function uses the + TranslatorSRI API. If that does not return a match, the function then downloads (if not already downloaded) a + yaml file of the current BioLink model and searches it. If that also does not return a match, then the function + formats the entity's label and returns it as the biolink type. Examples are shown below. For entities, + this function relies on the TranslatorSRI application (https://github.com/TranslatorSRI/NodeNormalization). + + Assumptions: If more than 1 BioLink type is provided, the function is designed to take the first one. + + EXAMPLE OUTPUT: + - ""CHEBI:16753" --> biolink:SmallMolecule + - "RO:0002512" --> biolink:translation_of + + Args: + entity: A string containing an entity CURIE (e.g., CHEBI:16753) or None. + entity_label: A string representing an entity label. + biolink_loc: A string containing a location to a biolink yaml file. + + Returns: + biolink_type: A string containing a BioLink model type for a node or an predication. + """ + + # check for biolink data being downloaded + biolink_file = 'https://raw.githubusercontent.com/biolink/biolink-model/master/biolink-model.yaml' + if not os.path.exists(biolink_loc + 'biolink-model.yaml'): data_downloader(biolink_file, biolink_loc) + biolink_data = yaml.load(open(biolink_loc + 'biolink-model.yaml'), Loader=yaml.FullLoader) + + # find entities bioLink type + entity = entity.replace('_', ':') + result = requests.get('https://nodenormalization-sri.renci.org/get_normalized_nodes', params={'curie': entity}) + res = result.json() + + if res[entity] is not None: biolink_type = res[entity]['type'][0] + else: # checks the biolink yaml for the entity CURIE + temp_idx = [k for k in biolink_data['slots'].keys() + if ('exact_mappings' in biolink_data['slots'][k].keys() + and entity in biolink_data['slots'][k]['exact_mappings']) + or ('narrow_mappings' in biolink_data['slots'][k].keys() + and entity in biolink_data['slots'][k]['narrow_mappings'])] + if len(temp_idx) > 0: biolink_type = 'biolink:{}'.format(temp_idx[0].replace(' ', '_')) + else: # checks the biolink yaml for the entity label + if entity_label is not None: + entity_label = entity_label.lower() + temp_str = [k for k in biolink_data['slots'].keys() if k == entity_label] + if len(temp_str) > 0: biolink_type = 'biolink:{}'.format(temp_str[0].replace(' ', '_')) + else: biolink_type = 'biolink:{}'.format(entity_label.replace(' ', '_')) + else: biolink_type = 'biolink:Other' + + return biolink_type diff --git a/tests/test_data_utils_miscellaneous.py b/tests/test_data_utils_miscellaneous.py index f2dece75..f291d8d5 100644 --- a/tests/test_data_utils_miscellaneous.py +++ b/tests/test_data_utils_miscellaneous.py @@ -177,6 +177,55 @@ def tests_obtains_entity_url_bad(self): return None + def tests_gets_biolink_information_entity(self): + """Tests the gets_biolink_information function when provided a valid entity CURIE.""" + + # set-up input + entity = 'CHEBI:16753'; entity_label = None + + # test function + res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') + self.assertEqual(res, 'biolink:SmallMolecule') + + return None + + def tests_gets_biolink_information_entitylabel(self): + """Tests the gets_biolink_information function when provided a valid entity CURIE and label are provided.""" + + # set-up input + entity = 'RO:0002436'; entity_label = 'molecularly interacts with' + + # test function + res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') + self.assertEqual(res, 'biolink:molecularly_interacts_with') + + return None + + def tests_gets_biolink_information_entitylabel2(self): + """Tests the gets_biolink_information function when provided a valid entity CURIE and label are provided.""" + + # set-up input + entity = 'rdfs:subClassOf'; entity_label = 'subclass of' + + # test function + res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') + self.assertEqual(res, 'biolink:subclass_of') + + return None + + def tests_gets_biolink_information_entitylabel3(self): + """Tests the gets_biolink_information function when provided a valid entity CURIE and label that cannot be + found in the model or API.""" + + # set-up input + entity = 'RO:0000000'; entity_label = None + + # test function + res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') + self.assertEqual(res, 'biolink:Other') + + return None + def tearDown(self): # remove temp directory From 5099cd688467e21f440b1b8cec3c8d5de7107245 Mon Sep 17 00:00:00 2001 From: "Tiffany J. Callahan" <callahantiff@gmail.com> Date: Thu, 30 Dec 2021 09:07:10 -0700 Subject: [PATCH 061/112] delaying build further --- .github/workflows/kg-build-part2.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kg-build-part2.yml b/.github/workflows/kg-build-part2.yml index cae4e76a..1dfab333 100644 --- a/.github/workflows/kg-build-part2.yml +++ b/.github/workflows/kg-build-part2.yml @@ -1,7 +1,7 @@ name: KG Build - Part 2 (Construct Knowledge Graphs) on: schedule: - - cron: '0 0 31 * *' # runs at 00:00:00 UTC on the second day of each month + - cron: '0 0 29 * *' # runs at 00:00:00 UTC on the second day of each month env: PROJECT_ID: ${{ secrets.GCE_PROJECT }} GCS_SERVICE_ACCOUNT: ${{ secrets.GCE_SA_KEY }} From c40096bff9f72f14ef3b0fc3e56e9973cbf950d9 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Thu, 30 Dec 2021 09:18:55 -0700 Subject: [PATCH 062/112] bumping lxml to match --- builds/build_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builds/build_requirements.txt b/builds/build_requirements.txt index ef1f0799..b991dbff 100644 --- a/builds/build_requirements.txt +++ b/builds/build_requirements.txt @@ -4,7 +4,7 @@ google==1.9.3 google-api-core==1.24.1 google-api-python-client~=1.7.9 google-cloud-storage==1.28.0 -lxml==4.6.3 +lxml>=4.6.5 networkx==2.4 numpy==1.18.1 openpyxl==3.0.3 From 7d3b38726d591e7be8d0b0f234cf62fbcefe4df9 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Sun, 2 Jan 2022 16:56:30 -0700 Subject: [PATCH 063/112] removed additional parameter --- generates_dependency_documents.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/generates_dependency_documents.py b/generates_dependency_documents.py index 290ab5b1..02e7581d 100644 --- a/generates_dependency_documents.py +++ b/generates_dependency_documents.py @@ -78,10 +78,6 @@ def information_getter(self) -> Tuple[Dict[str, str], Dict[str, str], Dict[str, ont_data[ont_edge] = input('Provide an owl or obo URL for this ontology: ') print('\n') - node_data_types = input('Provide the data types for each node in the edge (e.g. "class" or "entity" (for ' - 'data that is not from an ontology) each node in the edge separated by "-" --> ' - '"class-entity"): ') - print('\n') delimiter = input('Provide the character used to split each row into columns (e.g. "t" or ","): ') print('\n') @@ -124,10 +120,9 @@ def information_getter(self) -> Tuple[Dict[str, str], Dict[str, str], Dict[str, print('\n') # add edge data to dictionary - resource_data[edge_type] = '{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}'.format(identifier_prefix_information, - node_data_types, edge_relation, - delimiter, col_idx, id_maps, - evi_crit, filt_crit) + resource_data[edge_type] = '{0}|{1}|{2}|{3}|{4}|{5}|{6}'.format(identifier_prefix_information, + edge_relation, delimiter, col_idx, + id_maps, evi_crit, filt_crit) # get edge data sources edge_data[edge_type] = input('Provide a URL or file path to data used to create this edge: ') @@ -167,13 +162,11 @@ def main(): '\n\t(2) IdentifierPrefixInformation: A ";"-separated string used to update a prefix-identifier pair ' '(e.g., GO;GO). The first and second items contain BioRegistry prefixes. If one of the\n\t\texisting ' 'prefixes is correct leave its spot empty and if both are correct, type ";". All prefixes should be the ' - 'preferred prefix from the BioRegistry\n\t\t(https://bioregistry.io/registry/);\n\t(3) NodeDataTypes: A ' - 'label of "class" or "entity" for each node in an edge separated by "-" (e.g., "class-class"). The "class" ' - 'label\n\t\trepresents nodes from ontologies and "entity" represents nodes from other data sources;\n\t(4) ' - 'Relation: A Relation Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056)\n\t(5) ' - 'Delimiter: A character used to split rows from an input data source into columns (e.g., "t" for ' - 'tab-delimited data or "," for comma-delimited data);\n\t(6) ColumnIndexes: Two-column indexes separated by ' - '";" (e.g., "0;4" for the first and third columns in the input data source);\n\t(7) IdentifierMaps: A string ' + 'preferred prefix from the BioRegistry\n\t\t(https://bioregistry.io/registry/);\n\t(3) Relation: A Relation ' + 'Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056)\n\t(4) Delimiter: A ' + 'character used to split rows from an input data source into columns (e.g., "t" for ' + 'tab-delimited data or "," for comma-delimited data);\n\t(5) ColumnIndexes: Two-column indexes separated by ' + '";" (e.g., "0;4" for the first and third columns in the input data source);\n\t(6) IdentifierMaps: A string ' 'of mapping information for each node in an edge. For example, the string "2:mapping_file_1.txt;' '4:mapping_file_2.txt" means that the first node require\n\t\tdata contained in the 2nd column of the ' '"mapping_file_1.txt" and the second node requires data from the 4th column in the "mapping_file_2.txt" ' @@ -192,7 +185,7 @@ def main(): '"<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" in ' 'the example above)\n\t\t\t3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and ' '"9606" in the example above).\n\n\tAn example line from the resource_info.txt file is shown below:\n\t\t' - 'chemical-gene|;MESH_;|class-class|;MESH_;|class-class|RO_0002434|#|t|1;4|0:./resources/data_maps/' + 'chemical-gene|;MESH_;|RO_0002434|#|t|1;4|0:./resources/data_maps/' 'MESH_CHEBI_MAP.txt|None|7;==;9606\n\n(2) ontology_source_info.txt: This document contains a "|"-delimited ' 'line for each ontology source used, for example:\n\t"chemical|http://purl.obolibrary.org/obo/chebi.owl"' '\n\t"gene|http://purl.obolibrary.org/obo/so.owl"\n\n(3) edge_source_info.txt: This document contains a ' From 58103796ecc6d73e55ba9fc6e3a401a6217b046a Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Sun, 2 Jan 2022 17:18:56 -0700 Subject: [PATCH 064/112] lowering the case of the variables --- generates_dependency_documents.py | 32 +++++++++++++++--------------- pkt_kg/edge_list.py | 16 +++++++-------- resources/edge_source_list.txt | 4 ++-- resources/ontology_source_list.txt | 5 +++-- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/generates_dependency_documents.py b/generates_dependency_documents.py index 02e7581d..1408fdcc 100644 --- a/generates_dependency_documents.py +++ b/generates_dependency_documents.py @@ -157,35 +157,35 @@ def main(): 'you create three documents:\n\t\t(1) resource_info.txt\n\t\t(2) ontology_source_info.txt\n\t\t(3) ' 'edge_source_info.txt\nAn example of the data this program expects to find within each of these ' 'documents is shown below:\n\n(1) resource_info.txt: This document represents each edge type as a single ' - '"|" delimited string and contains a total of 9 items:\n\t(1) EdgeType: A string label for an edge ' + '"|" delimited string and contains a total of 9 items:\n\t(1) edge_type: A string label for an edge ' '(node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files' - '\n\t(2) IdentifierPrefixInformation: A ";"-separated string used to update a prefix-identifier pair ' + '\n\t(2) identifier_prefix_nformation: A ";"-separated string used to update a prefix-identifier pair ' '(e.g., GO;GO). The first and second items contain BioRegistry prefixes. If one of the\n\t\texisting ' 'prefixes is correct leave its spot empty and if both are correct, type ";". All prefixes should be the ' - 'preferred prefix from the BioRegistry\n\t\t(https://bioregistry.io/registry/);\n\t(3) Relation: A Relation ' - 'Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056)\n\t(4) Delimiter: A ' + 'preferred prefix from the BioRegistry\n\t\t(https://bioregistry.io/registry/);\n\t(3) relation: A Relation ' + 'Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056)\n\t(4) delimiter: A ' 'character used to split rows from an input data source into columns (e.g., "t" for ' - 'tab-delimited data or "," for comma-delimited data);\n\t(5) ColumnIndexes: Two-column indexes separated by ' - '";" (e.g., "0;4" for the first and third columns in the input data source);\n\t(6) IdentifierMaps: A string ' + 'tab-delimited data or "," for comma-delimited data);\n\t(5) column_indexes: Two-column indexes separated by ' + '";" (e.g., "0;4" for the first and third columns in the input data source);\n\t(5) IdentifierMaps: A string ' 'of mapping information for each node in an edge. For example, the string "2:mapping_file_1.txt;' '4:mapping_file_2.txt" means that the first node require\n\t\tdata contained in the 2nd column of the ' '"mapping_file_1.txt" and the second node requires data from the 4th column in the "mapping_file_2.txt" ' - 'file;\n\t(8) EvidenceCriteria: Evidence criteria that can be used to filter an input data source (e.g., ' + 'file;\n\t(6) evidence_criteria: Evidence criteria that can be used to filter an input data source (e.g., ' 'scores above a certain cut-off). An evidence set is composed of 3 pieces of ";"\n\t\t-separated ' 'information. Multiple filtering sets can be passed, where each set is separated by "::". Consider the ' 'following example: "4;!=;IEA::8;<;0.0001"):\n\t\t\t1. The index of the column to apply the evidence ' 'criteria to (e.g., "4" and "8" in the example above)\n\t\t\t2. The operator (i.e., "==", "!=", "<", ' '">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" in the ' 'example above).\n\t\t\t3.The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and ' - '"0.0001" in the example above);\n\t(9) Filtering criteria that can be used to filter an input data source ' - '(e.g., human proteins). An evidence set is composed of 3 pieces of ";"-separated information.\n\t\tMultiple ' - 'filtering sets can be passed as demonstrated by the example above, where each set is separated by "::". ' - 'Consider the following example: "5;==;P::7;==;9606"):\n\t\t\t1. The index of the column to apply the ' - 'evidence criteria to (e.g., "5" and "7" in the example above)\n\t\t\t2. The operator (i.e., "==", "!=", ' - '"<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" in ' - 'the example above)\n\t\t\t3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and ' - '"9606" in the example above).\n\n\tAn example line from the resource_info.txt file is shown below:\n\t\t' - 'chemical-gene|;MESH_;|RO_0002434|#|t|1;4|0:./resources/data_maps/' + '"0.0001" in the example above);\n\t(7) filtering_criteria: Criteria that can be used to filter an input ' + 'data source (e.g., human proteins). An evidence set is composed of 3 pieces of ";"-separated information.' + '\n\t\tMultiple filtering sets can be passed as demonstrated by the example above, where each set is ' + 'separated by "::". Consider the following example: "5;==;P::7;==;9606"):\n\t\t\t1. The index of the ' + 'column to apply the evidence criteria to (e.g., "5" and "7" in the example above)\n\t\t\t2. The operator ' + '(i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering ' + '(e.g., "==" and "==" in the example above)\n\t\t\t3. The value (i.e., "int", "float", "str", "list") to ' + 'filter on (e.g., "P" and "9606" in the example above).\n\n\tAn example line from the resource_info.txt file ' + 'is shown below:\n\t\tchemical-gene|;MESH_;|RO_0002434|#|t|1;4|0:./resources/data_maps/' 'MESH_CHEBI_MAP.txt|None|7;==;9606\n\n(2) ontology_source_info.txt: This document contains a "|"-delimited ' 'line for each ontology source used, for example:\n\t"chemical|http://purl.obolibrary.org/obo/chebi.owl"' '\n\t"gene|http://purl.obolibrary.org/obo/so.owl"\n\n(3) edge_source_info.txt: This document contains a ' diff --git a/pkt_kg/edge_list.py b/pkt_kg/edge_list.py index c4245516..3c26e529 100755 --- a/pkt_kg/edge_list.py +++ b/pkt_kg/edge_list.py @@ -55,15 +55,13 @@ def __init__(self, data_files: Dict[str, str], source_file: str) -> None: cols = ['"{}"'.format(x.strip()) for x in list(csv.reader([row], delimiter='|', quotechar='"'))[0]] key = cols[0].strip('"').strip("'") self.source_info[key] = {} - self.source_info[key]['source_labels'] = cols[1].strip('"').strip("'") - self.source_info[key]['data_type'] = cols[2].strip('"').strip("'") - self.source_info[key]['edge_relation'] = cols[3].strip('"').strip("'") - self.source_info[key]['uri'] = (cols[4].strip('"').strip("'"), cols[5].strip('"').strip("'")) - self.source_info[key]['delimiter'] = cols[6].strip('"').strip("'") - self.source_info[key]['column_idx'] = cols[7].strip('"').strip("'") - self.source_info[key]['identifier_maps'] = cols[8].strip('"').strip("'") - self.source_info[key]['evidence_criteria'] = cols[9].strip('"').strip("'") - self.source_info[key]['filter_criteria'] = cols[10].strip('"').strip("'") + self.source_info[key]['identifier_prefix_information'] = cols[1].strip('"').strip("'") + self.source_info[key]['relation'] = cols[2].strip('"').strip("'") + self.source_info[key]['delimiter'] = cols[3].strip('"').strip("'") + self.source_info[key]['column_indexes'] = cols[4].strip('"').strip("'") + self.source_info[key]['identifier_maps'] = cols[5].strip('"').strip("'") + self.source_info[key]['evidence_criteria'] = cols[6].strip('"').strip("'") + self.source_info[key]['filter_criteria'] = cols[7].strip('"').strip("'") self.source_info[key]['edge_list'] = [] source_file_data.close() diff --git a/resources/edge_source_list.txt b/resources/edge_source_list.txt index baaf89cb..b7c9dded 100644 --- a/resources/edge_source_list.txt +++ b/resources/edge_source_list.txt @@ -1,8 +1,8 @@ ##################################################################################################################################################### #### edge_source_info.txt (last updated: December 27, 2021) ### Each column is separated by a pipe (i.e., "|") and includes the following: -# EdgeType: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and ontology_source_list.txt files. -# URL: A string containing a URL to the primary data source for the edge. +# edge_type: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and ontology_source_list.txt files. +# url: A string containing a URL to the primary data source for the edge. ##################################################################################################################################################### chemical-disease|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chemicals_diseases.tsv chemical-gene|https://storage.googleapis.com/pheknowlator/current_build/data/original_data/CTD_chem_gene_ixns.tsv diff --git a/resources/ontology_source_list.txt b/resources/ontology_source_list.txt index 58385362..6dc71b1d 100644 --- a/resources/ontology_source_list.txt +++ b/resources/ontology_source_list.txt @@ -1,8 +1,9 @@ ################################################################################################################################################### #### ontology_source_info.txt (last updated: December 27, 2021) ### Each column is separated by a pipe (i.e., "|") and includes the following: -# Ontology: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and edge_source_list.txt files. -# URL: A string containing a URL to the ontology file. +# ontology: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and +edge_source_list.txt files. +# url: A string containing a URL to the ontology file. #################################################################################################################################################### phenotype|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/hp_with_imports.owl go|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/go_with_imports.owl From 9c96330d7c4b336d9fab721d1d3f858c7ce1279d Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 4 Jan 2022 14:51:54 -0700 Subject: [PATCH 065/112] extending bioregistry functionality --- pkt_kg/utils/data_utils.py | 25 +++++++++++++++++++++++-- tests/test_data_utils_miscellaneous.py | 14 +++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index 9535e355..c85513c5 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -501,11 +501,32 @@ def obtains_entity_url(prefix: str, identifier: Union[int, str]) -> str: prefix was provided. """ + entity_url = None; default_bioregistry_url = 'https://bioregistry.io/' + obo_ont_prefixes = ['BFO', 'CHEBI', 'DOID', 'GO', 'OBI', 'PATO', 'PO', 'PR', 'XAO', 'ZFA', 'AEO', 'AGRO', 'AISM', + 'AMPHX', 'APO', 'APOLLO_SV', 'ARO', 'BCO', 'BSPO', 'BTO', 'CARO', 'CDAO', 'CDNO', 'CHEMINF', + 'CHIRO', 'CHMO', 'CIDO', 'CIO', 'CL', 'CLAO', 'CLO', 'CLYH', 'CMO', 'COB', 'COLAO', 'CRO', + 'CTENO', 'CTO', 'CVDO', 'DDANAT', 'DDPHENO', 'DIDEO', 'DISDRIV', 'DPO', 'DRON', 'DUO', + 'ECAO', 'ECO', 'ECOCORE', 'ECTO', 'EMAPA', 'ENVO', 'EUPATH', 'EXO', 'FAO', 'FBBI', 'FBBT', + 'FBCV', 'FBDV', 'FIDEO', 'FLOPO', 'FMA', 'FOBI', 'FOODON', 'FOVT', 'FYPO', 'GECKO', + 'GENEPIO', 'GENO', 'GEO', 'GNO', 'HANCESTRO', 'HAO', 'HOM', 'HSAPDV', 'HSO', 'HTN', 'IAO', + 'ICEO', 'ICO', 'IDO', 'INO', 'LABO', 'LEPAO', 'MA', 'MAXO', 'MCO', 'MF', 'MFMO', 'MFOEM', + 'MFOMD', 'MI', 'MIAPA', 'MICRO', 'MMO', 'MMUSDV', 'MOD', 'MONDO', 'MOP', 'MP', 'MPATH', + 'MPIO', 'MRO', 'MS', 'NBO', 'NCBITAXON', 'NCIT', 'NCRO', 'NOMEN', 'OAE', 'OARCS', 'OBA', + 'OBCS', 'OBIB', 'OGG', 'OGMS', 'OGSF', 'OHD', 'OHMI', 'OHPI', 'OLATDV', 'OMIT', 'OMO', 'OMP', + 'OMRSE', 'ONE', 'ONS', 'ONTOAVIDA', 'ONTONEO', 'OOSTT', 'OPL', 'OPMI', 'ORNASEQ', 'OVAE', + 'PCO', 'PDRO', 'PDUMDV', 'PECO', 'PHIPO', 'PLANA', 'PLANP', 'PORO', 'PPO', 'PSDO', 'PSO', + 'PW', 'RBO', 'RO', 'RS', 'RXNO', 'SEPIO', 'SO', 'SPD', 'STATO', 'SWO', 'SYMP', 'TAXRANK', + 'TO', 'TRANS', 'TTO', 'TXPO', 'UBERON', 'UO', 'UPHENO', 'VO', 'VT', 'VTO', 'WBBT', 'WBLS', + 'WBPHENOTYPE', 'XCO', 'XLMOD', 'XPO', 'ZECO', 'ZFS', 'ZP', 'EPIO', 'GSSO', 'HP', 'KISAO', + 'MAMO', 'SBO', 'SCDO', 'SIBO', 'FIX', 'VARIO', 'OGI', 'REX', 'CEPH', 'EHDAA2', 'GAZ', 'RNAO', + 'UPA', 'ERO', 'IDOMAL', 'MIRO', 'TADS', 'TGMA', ] + try: res = requests.get('https://bioregistry.io/api/reference/' + prefix.lower() + ':' + str(identifier)).json() except JSONDecodeError: - raise ValueError('Error: Invalid prefix or identifier provided. Please check your input and try again.') - entity_url = res['providers']['bioregistry'] + if prefix.upper() in obo_ont_prefixes: entity_url = default_bioregistry_url + prefix + ':' + str(identifier) + else: raise ValueError('Error: Invalid prefix or identifier provided. Please check your input and try again.') + if not isinstance(entity_url, str): entity_url = res['providers']['bioregistry'] return entity_url diff --git a/tests/test_data_utils_miscellaneous.py b/tests/test_data_utils_miscellaneous.py index f291d8d5..dde9cd3b 100644 --- a/tests/test_data_utils_miscellaneous.py +++ b/tests/test_data_utils_miscellaneous.py @@ -166,11 +166,23 @@ def tests_obtains_entity_url_good(self): return None + def tests_obtains_entity_url_obo(self): + """Tests the obtains_entity_url method when an obo identifier is passed.""" + + # set-up input + prefix = 'pr'; identifier = 'A5D8V7' + + # test function + entity_uri = obtains_entity_url(prefix, identifier) + self.assertEqual(entity_uri, 'https://bioregistry.io/pr:A5D8V7') + + return None + def tests_obtains_entity_url_bad(self): """Tests the obtains_entity_url method when an invalid identifier is passed.""" # set-up input - prefix = 'chebi'; identifier = 't' + prefix = 'hpo'; identifier = 't' # test function self.assertRaises(ValueError, obtains_entity_url, prefix, identifier) From 76af5a68dd3dd4e56a31a4a5c294ee71b5311414 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 4 Jan 2022 14:52:48 -0700 Subject: [PATCH 066/112] finalizing data --- builds/data_to_download.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/builds/data_to_download.txt b/builds/data_to_download.txt index 9b606146..93ed62ff 100755 --- a/builds/data_to_download.txt +++ b/builds/data_to_download.txt @@ -24,6 +24,7 @@ mesh2021.nt, ftp://nlmpubs.nlm.nih.gov/online/mesh/rdf/2021/mesh2021.nt names.tsv, ftp://ftp.ebi.ac.uk/pub/databases/chebi/Flat_file_tab_delimited/names.tsv.gz # disease and phenotype identifiers disease_mappings.tsv, https://www.disgenet.org/static/disgenet_ap1/files/downloads/disease_mappings.tsv.gz +MGCONSO.RRF, https://ftp.ncbi.nlm.nih.gov/pub/medgen/MGCONSO.RRF.gz # human protein atlas/gtex tissue/cells - uberon + cell ontology + cell line ontology proteinatlas_search.tsv.gz, https://www.proteinatlas.org/api/search_download.php?search=&columns=g,eg,up,pe,rnatsm,rnaclsm,rnacasm,rnabrsm,rnabcsm,rnablsm,scl,t_RNA_adipose_tissue,t_RNA_adrenal_gland,t_RNA_amygdala,t_RNA_appendix,t_RNA_basal_ganglia,t_RNA_bone_marrow,t_RNA_breast,t_RNA_cerebellum,t_RNA_cerebral_cortex,t_RNA_cervix,_uterine,t_RNA_colon,t_RNA_corpus_callosum,t_RNA_ductus_deferens,t_RNA_duodenum,t_RNA_endometrium_1,t_RNA_epididymis,t_RNA_esophagus,t_RNA_fallopian_tube,t_RNA_gallbladder,t_RNA_heart_muscle,t_RNA_hippocampal_formation,t_RNA_hypothalamus,t_RNA_kidney,t_RNA_liver,t_RNA_lung,t_RNA_lymph_node,t_RNA_midbrain,t_RNA_olfactory_region,t_RNA_ovary,t_RNA_pancreas,t_RNA_parathyroid_gland,t_RNA_pituitary_gland,t_RNA_placenta,t_RNA_pons_and_medulla,t_RNA_prostate,t_RNA_rectum,t_RNA_retina,t_RNA_salivary_gland,t_RNA_seminal_vesicle,t_RNA_skeletal_muscle,t_RNA_skin_1,t_RNA_small_intestine,t_RNA_smooth_muscle,t_RNA_spinal_cord,t_RNA_spleen,t_RNA_stomach_1,t_RNA_testis,t_RNA_thalamus,t_RNA_thymus,t_RNA_thyroid_gland,t_RNA_tongue,t_RNA_tonsil,t_RNA_urinary_bladder,t_RNA_vagina,t_RNA_B-cells,t_RNA_dendritic_cells,t_RNA_granulocytes,t_RNA_monocytes,t_RNA_NK-cells,t_RNA_T-cells,t_RNA_total_PBMC,cell_RNA_A-431,cell_RNA_A549,cell_RNA_AF22,cell_RNA_AN3-CA,cell_RNA_ASC_diff,cell_RNA_ASC_TERT1,cell_RNA_BEWO,cell_RNA_BJ,cell_RNA_BJ_hTERT+,cell_RNA_BJ_hTERT+_SV40_Large_T+,cell_RNA_BJ_hTERT+_SV40_Large_T+_RasG12V,cell_RNA_CACO-2,cell_RNA_CAPAN-2,cell_RNA_Daudi,cell_RNA_EFO-21,cell_RNA_fHDF/TERT166,cell_RNA_HaCaT,cell_RNA_HAP1,cell_RNA_HBEC3-KT,cell_RNA_HBF_TERT88,cell_RNA_HDLM-2,cell_RNA_HEK_293,cell_RNA_HEL,cell_RNA_HeLa,cell_RNA_Hep_G2,cell_RNA_HHSteC,cell_RNA_HL-60,cell_RNA_HMC-1,cell_RNA_HSkMC,cell_RNA_hTCEpi,cell_RNA_hTEC/SVTERT24-B,cell_RNA_hTERT-HME1,cell_RNA_HUVEC_TERT2,cell_RNA_K-562,cell_RNA_Karpas-707,cell_RNA_LHCN-M2,cell_RNA_MCF7,cell_RNA_MOLT-4,cell_RNA_NB-4,cell_RNA_NTERA-2,cell_RNA_PC-3,cell_RNA_REH,cell_RNA_RH-30,cell_RNA_RPMI-8226,cell_RNA_RPTEC_TERT1,cell_RNA_RT4,cell_RNA_SCLC-21H,cell_RNA_SH-SY5Y,cell_RNA_SiHa,cell_RNA_SK-BR-3,cell_RNA_SK-MEL-30,cell_RNA_T-47d,cell_RNA_THP-1,cell_RNA_TIME,cell_RNA_U-138_MG,cell_RNA_U-2_OS,cell_RNA_U-2197,cell_RNA_U-251_MG,cell_RNA_U-266/70,cell_RNA_U-266/84,cell_RNA_U-698,cell_RNA_U-87_MG,cell_RNA_U-937,cell_RNA_WM-115,blood_RNA_basophil,blood_RNA_classical_monocyte,blood_RNA_eosinophil,blood_RNA_gdT-cell,blood_RNA_intermediate_monocyte,blood_RNA_MAIT_T-cell,blood_RNA_memory_B-cell,blood_RNA_memory_CD4_T-cell,blood_RNA_memory_CD8_T-cell,blood_RNA_myeloid_DC,blood_RNA_naive_B-cell,blood_RNA_naive_CD4_T-cell,blood_RNA_naive_CD8_T-cell,blood_RNA_neutrophil,blood_RNA_NK-cell,blood_RNA_non-classical_monocyte,blood_RNA_plasmacytoid_DC,blood_RNA_T-reg,blood_RNA_total_PBMC,brain_RNA_amygdala,brain_RNA_basal_ganglia,brain_RNA_cerebellum,brain_RNA_cerebral_cortex,brain_RNA_hippocampal_formation,brain_RNA_hypothalamus,brain_RNA_midbrain,brain_RNA_olfactory_region,brain_RNA_pons_and_medulla,brain_RNA_thalamus&format=tsv GTEx_Analysis_2017-06-05_v8_RNASeQCv1.1.9_gene_median_tpm.gct, https://storage.googleapis.com/gtex_analysis_v8/rna_seq_data/GTEx_Analysis_2017-06-05_v8_RNASeQCv1.1.9_gene_median_tpm.gct.gz @@ -39,14 +40,8 @@ genomic_sequence_ontology_mappings.xlsx, https://storage.googleapis.com/pheknowl human_pro_classes.html, https://sparql.proconsortium.org/virtuoso/sparql?query=PREFIX+obo%3A+%3Chttp%3A%2F%2Fpurl.obolibrary.org%2Fobo%2F%3E%0D%0A%0D%0ASELECT+%3FPRO_term%0D%0AFROM+%3Chttp%3A%2F%2Fpurl.obolibrary.org%2Fobo%2Fpr%3E%0D%0AWHERE+%7B%0D%0A+++++++%3FPRO_term+rdf%3Atype+owl%3AClass+.%0D%0A+++++++%3FPRO_term+rdfs%3AsubClassOf+%3Frestriction+.%0D%0A+++++++%3Frestriction+owl%3AonProperty+obo%3ARO_0002160+.%0D%0A+++++++%3Frestriction+owl%3AsomeValuesFrom+obo%3ANCBITaxon_9606+.%0D%0A%0D%0A+++++++%23+use+this+to+filter-out+things+like+hgnc+ids%0D%0A+++++++FILTER+%28regex%28%3FPRO_term%2C%22http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2F*%22%29%29+.%0D%0A%7D&format=text%2Fhtml&debug= # clinvar variant-diseases and phenotypes variant_summary.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz -submission_summary.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz -disease_names, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names var_citations.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt allele_gene.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz -gene_specific_summary.txt, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt -gene_condition_source_id, https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id -# human phenotype and disease mapping files -MGCONSO.RRF, https://ftp.ncbi.nlm.nih.gov/pub/medgen/MGCONSO.RRF.gz # uniprot protein-cofactor and protein-catalyst uniprot-cofactor-catalyst.tab, https://www.uniprot.org/uniprot/?query=&fil=organism%3A%22Homo%20sapiens%20(Human)%20%5B9606%5D%22&columns=id%2Creviewed%2Centry%20name%2Cdatabase(PRO)%2Cchebi(Cofactor)%2Cchebi(Catalytic%20activity)&format=tab From 0c27d3cbb9b7af5e3e4f43e368c19f43beef38ac Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 4 Jan 2022 14:53:11 -0700 Subject: [PATCH 067/112] updating clinvar resource filenames --- resources/edge_source_list.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/edge_source_list.txt b/resources/edge_source_list.txt index b7c9dded..ab977b3b 100644 --- a/resources/edge_source_list.txt +++ b/resources/edge_source_list.txt @@ -35,6 +35,6 @@ protein-protein|https://storage.googleapis.com/pheknowlator/current_build/data/o rna-anatomy|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt rna-cell|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt rna-protein|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENSEMBL_TRANSCRIPT_PROTEIN_ONTOLOGY_MAP.txt -variant-disease|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt -variant-gene|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt -variant-phenotype|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt \ No newline at end of file +variant-disease|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt +variant-gene|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_EDGES.txt +variant-phenotype|https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt \ No newline at end of file From 2ebfbd9f205f895e618b576b17db3c8d50cde2ca Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 4 Jan 2022 14:53:33 -0700 Subject: [PATCH 068/112] modifying header and updating maps --- resources/resource_info.txt | 101 +++++++++++++++++------------------- 1 file changed, 49 insertions(+), 52 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index f2368774..20d651bd 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -1,64 +1,61 @@ ###################################################################################################################################################################################### #### resource_info.txt (last updated: December 27, 2021) ### Each column is separated by a pipe (i.e., "|") and includes the following: -# EdgeType: A string label for an edge (node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files. -# IdentifierPrefixInformation: A ";"-separated string used to update a prefix-identifier pair (e.g., GO;GO). The first and second items contain BioRegistry prefixes -# that should be used for the subject and object nodes of an edge, respectively. If one of the existing prefixes is correct leave its spot -# empty and if both are correct, type ";".All prefixes should be the preferred prefix from the BioRegistry (https://bioregistry.io/registry/). -# NodeDataTypes: A label of "class" or "entity" for each node in an edge separated by "-" (e.g., "class-class"). The "class" label represents nodes from -# ontologies and "entity" represents nodes from other data sources. -# Relation: A Relation Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056). -# Delimiter: A character used to split rows from an input data source into columns (e.g., "t" for tab-delimited data or "," for comma-delimited data). -# ColumnIndexes: Two-column indexes separated by ";" (e.g., "0;4" for the first and third columns in the input data source). -# IdentifierMaps: A string of mapping information for each node in an edge. For example, the string "2:mapping_file_1.txt;4:mapping_file_2.txt" means that +# edge_type: A string label for an edge (node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files. +# identifier_prefixes: A ";"-separated string where the first item is the final prefix for the subject node and the second is the final prefix for the object node. +# All prefixes should be the preferred prefix from the BioRegistry (https://bioregistry.io/registry/). +# relation: An OBO Foundry ontology CURIE (e.g., RO_0000056). +# delimiter: A character used to split rows from an input data source into columns (e.g., "t" for tab-delimited data or "," for comma-delimited data). +# column_indexes: Two-column indexes separated by ";" (e.g., "0;4" for the first and third columns in the input data source). +# identifier_maps: A string of mapping information for each node in an edge. For example, the string "2:mapping_file_1.txt;4:mapping_file_2.txt" means that # the first node requires data contained in the 2nd column of the "mapping_file_1.txt" and the second node requires data from the 4th column # in the "mapping_file_2.txt" file. -# EvidenceCriteria: Evidence criteria that can be used to filter an input data source (e.g., scores above a certain cut-off). An evidence set is composed of 3 +# evidence_criteria: Evidence criteria that can be used to filter an input data source (e.g., scores above a certain cut-off). An evidence set is composed of 3 # pieces of ";"-separated information. Multiple evidence sets can be passed, where each set is separated by "::". Consider the following # example: "4;!=;IEA::8;<;0.0001": -# 1. The index of the column to apply the evidence criteria to (e.g., "4" and "8" in the example above) +# 1. The index of the column to apply the evidence criteria to (e.g., "4" and "8" in the example above). # 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" -# in the example above) -# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and "0.0001" in the example above) -# FilterCriteria: Filtering criteria that can be used to filter an input data source (e.g., human proteins). An evidence set is composed of 3 pieces of ";"- +# in the example above). +# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and "0.0001" in the example above). +# filter_criteria: Filtering criteria that can be used to filter an input data source (e.g., human proteins). An evidence set is composed of 3 pieces of ";"- # separated information. Multiple filtering sets can be passed, where each set is separated by "::". Consider the following example: # "5;==;P::7;==;9606"): -# 1. The index of the column to apply the evidence criteria to (e.g., "5" and "7" in the example above) +# 1. The index of the column to apply the evidence criteria to (e.g., "5" and "7" in the example above). # 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" -# in the example above) -# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above) +# in the example above). +# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above). ###################################################################################################################################################################################### -chemical-disease|MESH;|class-class|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None -chemical-gene|MESH;NCBIGene|class-entity|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); -chemical-gobp|MESH;GO|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Biological Process -chemical-gocc|;MESH;GO|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Cellular Component -chemical-gomf|MESH;GO|class-class|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Molecular Function -chemical-pathway|CHEBI;|class-entity|RO_0000056|t|0;1|None|None|5;==;Homo sapiens -chemical-phenotype|MESH;|class-class|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None -chemical-protein|MESH;|class-class|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); -chemical-rna|MESH;|class-entity|RO_0002434|t|0;3|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); -disease-phenotype|;HP|class-class|RO_0002200|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|2;!=;NOT -gene-disease|NCBIGene;|entity-class|RO_0003302|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|6;!=;group -gene-gene|NCBIGene;NCBIGene|entity-entity|RO_0002435|t|0;1|0:./resources/processed_data/OTHER_GENE_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/ENSEMBL_GENE_ENTREZ_GENE_MAP.txt|None|None -gene-pathway|NCBIGene;|entity-entity|RO_0000056|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); -gene-phenotype|NCBIGene;|entity-class|RO_0003302|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|6;!=;group -gene-protein|NCBIGene;|entity-class|RO_0002205|t|0;1|None|None|4;==;protein-coding -gene-rna|NCBIGene;|entity-entity|RO_0002511|t|0;1|None|None|None -gobp-pathway|GO;|class-entity|RO_0009501|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -pathway-gocc|;GO|entity-class|RO_0002180|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -pathway-gomf|;GO|entity-class|RO_0000085|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -protein-anatomy|;|class-class|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;anatomy -protein-catalyst|;|class-class|RO_0002436|t|0;1|None|None|None|None -protein-cell|;|class-class|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;cell line -protein-cofactor|;|class-class|RO_0002436|t|0;1|None|None|None|None -protein-gobp|;GO|class-class|RO_0000056|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein -protein-gocc|;GO|class-class|RO_0001025|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein -protein-gomf|;GO|class-class|RO_0000085|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein -protein-pathway|;|class-entity|RO_0000056|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens -protein-protein|;|class-class|RO_0002436|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|None|None -rna-anatomy|;|entity-class|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy -rna-cell|;|entity-class|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line. -rna-protein|;|entity-class|RO_0002513|https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=|http://purl.obolibrary.org/obo/|t|0;1|None|None|4;==;protein-coding. -variant-disease|rs;|entity-class|RO_0003302|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc. -variant-gene|rs;NCBIGene|entity-entity|RO_0002566|t|9;3|None|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]|9;!=;-1::3;!=;-1::16;==;GRCh38::8-9;dedup;desc. -variant-phenotype|rs;|entity-class|RO_0003302|t|9;12|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|24;in;["criteria provided, multiple submitters, no conflicts", "reviewed by expert panel", "practice guideline"]::7;==;1|9;!=;-1::16;==;GRCh38::8-9;dedup;desc \ No newline at end of file +chemical-disease|CHEBI;|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/DISEASE_MONDO_MAP.txt|9;!=;''|None +chemical-gene|CHEBI;NCBIGene|RO_0002434|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('gene'); +chemical-gobp|CHEBI;GO|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Biological Process +chemical-gocc|CHEBI;GO|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Cellular Component +chemical-gomf|CHEBI;GO|RO_0002436|t|1;5|0:./resources/processed_data/MESH_CHEBI_MAP.txt|None|3;==;Molecular Function +chemical-pathway|CHEBI;reactome|RO_0000056|t|0;1|None|None|5;==;Homo sapiens +chemical-phenotype|CHEBI;HP|RO_0002606|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|9;!=;''|None +chemical-protein|CHEBI;PR|RO_0002434|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); +chemical-rna|CHEBI;ensembl|RO_0002434|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); +disease-phenotype|MONDO;HP|RO_0002200|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|2;!=;NOT +gene-disease|NCBIGene;MONDO|RO_0003302|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|6;!=;group +gene-gene|NCBIGene;NCBIGene|RO_0002435|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt|None|None +gene-pathway|NCBIGene;reactome|RO_0000056|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); +gene-phenotype|NCBIGene;HP|RO_0003302|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|6;!=;group +gene-protein|NCBIGene;PR|RO_0002205|t|4;1|None|None|3;==;protein-coding +gene-rna|NCBIGene;ensembl|RO_0002511|t|6;1|None|None|None +gobp-pathway|GO;reactome|RO_0009501|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +pathway-gocc|reactome;GO|RO_0002180|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +pathway-gomf|reactome;GO|RO_0000085|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +protein-anatomy|PR;UBERON|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;anatomy +protein-catalyst|PR;CHEBI|RO_0002436|t|0;1|None|None|None|None +protein-cell|PR;CL|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;cell line +protein-cofactor|PR;CHEBI|RO_0002436|t|0;1|None|None|None|None +protein-gobp|PR;GO|RO_0000056|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;P::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-gocc|PR;GO|RO_0001025|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;C::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-gomf|PR;GO|RO_0000085|t|1;4|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|8;==;F::12;==;taxon:9606::3;not in;["NOT"]::11;==;protein +protein-pathway|PR;reactome|RO_0000056|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt|None|5;==;Homo sapiens +protein-protein|PR;PR|RO_0002436|''|0;1|0:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/STRING_PRO_ONTOLOGY_MAP.txt|None|None +rna-anatomy|ensembl;UBERON|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy +rna-cell|ensembl;CL|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line. +rna-protein|ensembl;PR|RO_0002513|t|4;1|None|None|3;==;protein-coding +variant-disease|clinvar;MONDO|RO_0003302|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None +variant-gene|clinvar;NCBIGene|RO_0002566|t|9;3|None|None|None +variant-phenotype|clinvar;HP|RO_0003302|t|9;12|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|None \ No newline at end of file From 286daffaa14ca1c823536ce21c79eccbcfa38b1f Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 4 Jan 2022 15:21:52 -0700 Subject: [PATCH 069/112] better logic for ids missing from bioregistry --- pkt_kg/utils/data_utils.py | 11 +++++++---- tests/test_data_utils_miscellaneous.py | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index c85513c5..06113110 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -485,13 +485,14 @@ def sublist_creator(actors: Union[Dict, List], chunk_size: int) -> List: return updated_lists -def obtains_entity_url(prefix: str, identifier: Union[int, str]) -> str: +def obtains_entity_url(prefix: str, identifier: Union[int, str], url: Optional[str] = None) -> str: """Function takes a prefix and identifier for an entity, looks it up in the BioRegistry API and returns a resolvable URL. Information on the BioRegistry can be found here: https://bioregistry.io/. Args: prefix: A string containing the prefix or name of a resources (e.g., "chebi"). identifier: A string or integer containing an entity identifier (e.g., "138488"). + url: A string containing a url. Returns: entity_url: A string containing a valid BioRegistry URL (e.g., ). @@ -501,7 +502,8 @@ def obtains_entity_url(prefix: str, identifier: Union[int, str]) -> str: prefix was provided. """ - entity_url = None; default_bioregistry_url = 'https://bioregistry.io/' + prefix = prefix.lower(); identifier = str(identifier); res = None; entity_url = None + default_bioregistry_url = 'https://bioregistry.io/'; obo_url = 'http://purl.obolibrary.org/obo/' obo_ont_prefixes = ['BFO', 'CHEBI', 'DOID', 'GO', 'OBI', 'PATO', 'PO', 'PR', 'XAO', 'ZFA', 'AEO', 'AGRO', 'AISM', 'AMPHX', 'APO', 'APOLLO_SV', 'ARO', 'BCO', 'BSPO', 'BTO', 'CARO', 'CDAO', 'CDNO', 'CHEMINF', 'CHIRO', 'CHMO', 'CIDO', 'CIO', 'CL', 'CLAO', 'CLO', 'CLYH', 'CMO', 'COB', 'COLAO', 'CRO', @@ -522,9 +524,10 @@ def obtains_entity_url(prefix: str, identifier: Union[int, str]) -> str: 'UPA', 'ERO', 'IDOMAL', 'MIRO', 'TADS', 'TGMA', ] try: - res = requests.get('https://bioregistry.io/api/reference/' + prefix.lower() + ':' + str(identifier)).json() + res = requests.get('https://bioregistry.io/api/reference/' + prefix + ':' + identifier).json() except JSONDecodeError: - if prefix.upper() in obo_ont_prefixes: entity_url = default_bioregistry_url + prefix + ':' + str(identifier) + if prefix.upper() in obo_ont_prefixes: entity_url = obo_url + prefix.upper() + '_' + identifier + elif url is not None: entity_url = url else: raise ValueError('Error: Invalid prefix or identifier provided. Please check your input and try again.') if not isinstance(entity_url, str): entity_url = res['providers']['bioregistry'] diff --git a/tests/test_data_utils_miscellaneous.py b/tests/test_data_utils_miscellaneous.py index dde9cd3b..86d7711a 100644 --- a/tests/test_data_utils_miscellaneous.py +++ b/tests/test_data_utils_miscellaneous.py @@ -174,11 +174,11 @@ def tests_obtains_entity_url_obo(self): # test function entity_uri = obtains_entity_url(prefix, identifier) - self.assertEqual(entity_uri, 'https://bioregistry.io/pr:A5D8V7') + self.assertEqual(entity_uri, 'http://purl.obolibrary.org/obo/PR_A5D8V7') return None - def tests_obtains_entity_url_bad(self): + def tests_obtains_entity_url_bad1(self): """Tests the obtains_entity_url method when an invalid identifier is passed.""" # set-up input @@ -189,6 +189,18 @@ def tests_obtains_entity_url_bad(self): return None + def tests_obtains_entity_url_bad2(self): + """Tests the obtains_entity_url method when an invalid identifier is passed, but a valid url is passed.""" + + # set-up input + prefix = 'swrl'; identifier = 'Variable'; url = 'http://www.w3.org/2003/11/swrl#Variable' + + # test function + entity_uri = obtains_entity_url(prefix, identifier, url) + self.assertEqual(entity_uri, 'http://www.w3.org/2003/11/swrl#Variable') + + return None + def tests_gets_biolink_information_entity(self): """Tests the gets_biolink_information function when provided a valid entity CURIE.""" From da0569c3a17927864a491e4a3e38c4aa88e6c433 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 4 Jan 2022 15:22:47 -0700 Subject: [PATCH 070/112] removing unused arg --- pkt_kg/utils/data_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index 06113110..77571e28 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -503,7 +503,7 @@ def obtains_entity_url(prefix: str, identifier: Union[int, str], url: Optional[s """ prefix = prefix.lower(); identifier = str(identifier); res = None; entity_url = None - default_bioregistry_url = 'https://bioregistry.io/'; obo_url = 'http://purl.obolibrary.org/obo/' + obo_url = 'http://purl.obolibrary.org/obo/' obo_ont_prefixes = ['BFO', 'CHEBI', 'DOID', 'GO', 'OBI', 'PATO', 'PO', 'PR', 'XAO', 'ZFA', 'AEO', 'AGRO', 'AISM', 'AMPHX', 'APO', 'APOLLO_SV', 'ARO', 'BCO', 'BSPO', 'BTO', 'CARO', 'CDAO', 'CDNO', 'CHEMINF', 'CHIRO', 'CHMO', 'CIDO', 'CIO', 'CL', 'CLAO', 'CLO', 'CLYH', 'CMO', 'COB', 'COLAO', 'CRO', From 431ab1137dc89f86f5aed8f41ce71f7f404af3ad Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Tue, 4 Jan 2022 17:00:27 -0700 Subject: [PATCH 071/112] adding jsonl functions --- pkt_kg/utils/__init__.py | 2 +- pkt_kg/utils/data_utils.py | 43 ++++++++++++++++++++++++++ tests/test_data_utils_miscellaneous.py | 35 +++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/pkt_kg/utils/__init__.py b/pkt_kg/utils/__init__.py index 8f6e690a..61d9f690 100644 --- a/pkt_kg/utils/__init__.py +++ b/pkt_kg/utils/__init__.py @@ -9,7 +9,7 @@ __all__ = ['url_download', 'ftp_url_download', 'gzipped_ftp_url_download', 'zipped_url_download', 'gzipped_url_download', 'data_downloader', 'explodes_data', 'chunks', 'metadata_dictionary_mapper', 'metadata_api_mapper', 'genomic_id_mapper', 'outputs_dictionary_data', 'obtains_entity_url', - 'gets_biolink_information', 'gets_ontology_statistics', 'gets_ontology_classes', + 'gets_biolink_information', 'gets_ontology_statistics', 'gets_ontology_classes', 'load_jsonl', 'dump_jsonl', 'gets_deprecated_ontology_classes', 'gets_object_properties', 'gets_ontology_class_dbxrefs', 'gets_ontology_class_synonyms', 'merges_ontologies', 'ontology_file_formatter', 'adds_edges_to_graph', 'remove_edges_from_graph', 'gets_entity_ancestors', 'connected_components', 'removes_self_loops', diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index 77571e28..670dd2eb 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -26,8 +26,12 @@ * obtains_entity_url * gets_biolink_information +Inputs data +* load_jsonl + Outputs data * outputs_dictionary_data +* dump_jsonl """ # import needed libraries @@ -583,3 +587,42 @@ def gets_biolink_information(entity: str, entity_label: Optional[str] = None, bi else: biolink_type = 'biolink:Other' return biolink_type + + +def dump_jsonl(data: List, output_path: str) -> None: + """Write list of objects to a JSON lines file. This function was modified from: + https://galea.medium.com/how-to-love-jsonl-using-json-line-format-in-your-workflow-b6884f65175b + + Args: + data: A list of Dict objects. + output_path: A string containing a location to write data to. + + Returns: + None. + """ + + with open(output_path, 'a+', encoding='utf-8') as f: + for line in data: + json_record = json.dumps(line, ensure_ascii=False) + f.write(json_record + '\n') + + return None + + +def load_jsonl(input_path: str) -> Dict: + """Read list of objects from a JSON lines file. This function was modified from: + https://galea.medium.com/how-to-love-jsonl-using-json-line-format-in-your-workflow-b6884f65175b + + Args: + input_path: A string containing a location to a jsonl file. + + Returns: + data_dict: A Dict object of the data contained in the object pointed to by input_path. + """ + + data_dict: Dict = dict() + with open(input_path, 'r', encoding='utf-8') as f: + for line in f: + data_dict.update(**json.loads(line.rstrip('\n|\r'))) + + return data_dict diff --git a/tests/test_data_utils_miscellaneous.py b/tests/test_data_utils_miscellaneous.py index 86d7711a..f7cfe1f6 100644 --- a/tests/test_data_utils_miscellaneous.py +++ b/tests/test_data_utils_miscellaneous.py @@ -250,6 +250,41 @@ def tests_gets_biolink_information_entitylabel3(self): return None + def tests_dump_jsonl(self): + """Tests the dump_jsonl function.""" + + # set-up input + out_location = self.dir_loc + '/out.jsonl' + + # test function + for url in ['https://chordanalytics.ca/', 'https://github.com/agalea91']: + webpage_data = {'page_url': url, 'status_code': 200} + dump_jsonl([webpage_data], out_location) + + self.assertTrue(os.path.exists(out_location)) + self.assertTrue(os.stat(out_location).st_size > 0) + + return None + + def tests_load_jsonl(self): + """Tests the load_jsonl function.""" + + # set-up input + out_location = self.dir_loc + '/out.jsonl' + for url in ['https://chordanalytics.ca/', 'https://github.com/agalea91']: + webpage_data = {url: {'status_code': 200}} + dump_jsonl([webpage_data], out_location) + + # test function + data_dict = load_jsonl(out_location) + test_dict = {'https://chordanalytics.ca/': {'status_code': 200}, + 'https://github.com/agalea91': {'status_code': 200}} + + self.assertIsInstance(data_dict, dict) + self.assertEqual(data_dict, test_dict) + + return None + def tearDown(self): # remove temp directory From 2d26f0d23453606905b35fc89f04e005060c74df Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Thu, 6 Jan 2022 09:58:57 -0700 Subject: [PATCH 072/112] updating definitions --- generates_dependency_documents.py | 44 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/generates_dependency_documents.py b/generates_dependency_documents.py index 1408fdcc..be362fd6 100644 --- a/generates_dependency_documents.py +++ b/generates_dependency_documents.py @@ -159,33 +159,31 @@ def main(): 'documents is shown below:\n\n(1) resource_info.txt: This document represents each edge type as a single ' '"|" delimited string and contains a total of 9 items:\n\t(1) edge_type: A string label for an edge ' '(node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files' - '\n\t(2) identifier_prefix_nformation: A ";"-separated string used to update a prefix-identifier pair ' - '(e.g., GO;GO). The first and second items contain BioRegistry prefixes. If one of the\n\t\texisting ' - 'prefixes is correct leave its spot empty and if both are correct, type ";". All prefixes should be the ' - 'preferred prefix from the BioRegistry\n\t\t(https://bioregistry.io/registry/);\n\t(3) relation: A Relation ' - 'Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056)\n\t(4) delimiter: A ' - 'character used to split rows from an input data source into columns (e.g., "t" for ' - 'tab-delimited data or "," for comma-delimited data);\n\t(5) column_indexes: Two-column indexes separated by ' - '";" (e.g., "0;4" for the first and third columns in the input data source);\n\t(5) IdentifierMaps: A string ' - 'of mapping information for each node in an edge. For example, the string "2:mapping_file_1.txt;' - '4:mapping_file_2.txt" means that the first node require\n\t\tdata contained in the 2nd column of the ' - '"mapping_file_1.txt" and the second node requires data from the 4th column in the "mapping_file_2.txt" ' - 'file;\n\t(6) evidence_criteria: Evidence criteria that can be used to filter an input data source (e.g., ' - 'scores above a certain cut-off). An evidence set is composed of 3 pieces of ";"\n\t\t-separated ' - 'information. Multiple filtering sets can be passed, where each set is separated by "::". Consider the ' - 'following example: "4;!=;IEA::8;<;0.0001"):\n\t\t\t1. The index of the column to apply the evidence ' - 'criteria to (e.g., "4" and "8" in the example above)\n\t\t\t2. The operator (i.e., "==", "!=", "<", ' - '">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" in the ' - 'example above).\n\t\t\t3.The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and ' - '"0.0001" in the example above);\n\t(7) filtering_criteria: Criteria that can be used to filter an input ' - 'data source (e.g., human proteins). An evidence set is composed of 3 pieces of ";"-separated information.' - '\n\t\tMultiple filtering sets can be passed as demonstrated by the example above, where each set is ' - 'separated by "::". Consider the following example: "5;==;P::7;==;9606"):\n\t\t\t1. The index of the ' + '\n\t(2) prefixes: A ";"-separated string where the first item is the final prefix for the subject node and ' + 'the second is the final prefix for the object node. . All prefixes should be the ' + 'preferred prefix from the BioRegistry\n\t\t(https://bioregistry.io/registry/);\n\t(3) relation: An OBO ' + 'Foundry ontology CURIE (e.g., RO_0000056)\n\t(4) delimiter: A character used to split rows from an input ' + 'data source into columns (e.g., "t" for tab-delimited data or "," for comma-delimited data);\n\t(5) ' + 'column_indexes: Two-column indexes separated by ";" (e.g., "0;4" for the first and third columns in the ' + 'input data source);\n\t(5) IdentifierMaps: A string of mapping information for each node in an edge. For ' + 'example, the string "2:mapping_file_1.txt;4:mapping_file_2.txt" means that the first node require\n\t\tdata ' + 'contained in the 2nd column of the "mapping_file_1.txt" and the second node requires data from the 4th ' + 'column in the "mapping_file_2.txt" file;\n\t(6) evidence_criteria: Evidence criteria that can be used to ' + 'filter an input data source (e.g., scores above a certain cut-off). An evidence set is composed of 3 pieces ' + 'of ";"\n\t\t-separated information. Multiple filtering sets can be passed, where each set is separated by ' + '"::". Consider the following example: "4;!=;IEA::8;<;0.0001"):\n\t\t\t1. The index of the column to apply ' + 'the evidence criteria to (e.g., "4" and "8" in the example above)\n\t\t\t2. The operator (i.e., "==", ' + '"!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and ' + '"<" in the example above).\n\t\t\t3.The value (i.e., "int", "float", "str", "list") to filter on (e.g., ' + '"IEA" and "0.0001" in the example above);\n\t(7) filtering_criteria: Criteria that can be used to filter ' + 'an input data source (e.g., human proteins). An evidence set is composed of 3 pieces of ";"-separated ' + 'information. \n\t\tMultiple filtering sets can be passed as demonstrated by the example above, where each ' + 'set is separated by "::". Consider the following example: "5;==;P::7;==;9606"):\n\t\t\t1. The index of the ' 'column to apply the evidence criteria to (e.g., "5" and "7" in the example above)\n\t\t\t2. The operator ' '(i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering ' '(e.g., "==" and "==" in the example above)\n\t\t\t3. The value (i.e., "int", "float", "str", "list") to ' 'filter on (e.g., "P" and "9606" in the example above).\n\n\tAn example line from the resource_info.txt file ' - 'is shown below:\n\t\tchemical-gene|;MESH_;|RO_0002434|#|t|1;4|0:./resources/data_maps/' + 'is shown below:\n\t\tchemical-gene|;MESH;|RO_0002434|#|t|1;4|0:./resources/data_maps/' 'MESH_CHEBI_MAP.txt|None|7;==;9606\n\n(2) ontology_source_info.txt: This document contains a "|"-delimited ' 'line for each ontology source used, for example:\n\t"chemical|http://purl.obolibrary.org/obo/chebi.owl"' '\n\t"gene|http://purl.obolibrary.org/obo/so.owl"\n\n(3) edge_source_info.txt: This document contains a ' From b813068bce8fe1d7800f6c6a3e4b70996e1aaf16 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Thu, 6 Jan 2022 09:59:33 -0700 Subject: [PATCH 073/112] updating variable names --- resources/resource_info.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 20d651bd..98b643f7 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -2,7 +2,7 @@ #### resource_info.txt (last updated: December 27, 2021) ### Each column is separated by a pipe (i.e., "|") and includes the following: # edge_type: A string label for an edge (node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files. -# identifier_prefixes: A ";"-separated string where the first item is the final prefix for the subject node and the second is the final prefix for the object node. +# prefixes: A ";"-separated string where the first item is the final prefix for the subject node and the second is the final prefix for the object node. # All prefixes should be the preferred prefix from the BioRegistry (https://bioregistry.io/registry/). # relation: An OBO Foundry ontology CURIE (e.g., RO_0000056). # delimiter: A character used to split rows from an input data source into columns (e.g., "t" for tab-delimited data or "," for comma-delimited data). From 4b83debcbda1f0defff941a3358ea74c2258cb15 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 7 Jan 2022 21:17:07 -0700 Subject: [PATCH 074/112] renaming node metadata directory and files --- .gitignore | 4 +- Main.py | 8 ++-- README.rst | 11 ++++-- main.ipynb | 20 +++++----- resources/metadata/README.md | 42 ++++++++++++++++++++ resources/node_data/README.md | 54 ------------------------- tests/test_metadata.py | 74 +++++++++++++++++------------------ 7 files changed, 102 insertions(+), 111 deletions(-) create mode 100644 resources/metadata/README.md delete mode 100644 resources/node_data/README.md diff --git a/.gitignore b/.gitignore index 57de4583..ebcd7a2d 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,7 @@ scratch*.py /resources/embeddings/* /resources/knowledge_graphs/ /resources/kr_model/ -/resources/node_data/* +/resources/metadata/* /resources/ontologies/* /resources/owl_decoding/* /resources/processed_data/* @@ -60,7 +60,7 @@ scratch*.py !/resources/edge_data/README.md !/resources/embeddings/README.md !/resources/knowledge_graphs/README.md -!/resources/node_data/README.md +!/resources/metadata/README.md !/resources/ontologies/ontology_source_metadata.txt !/resources/ontologies/README.md !/resources/owl_decoding/README.md diff --git a/Main.py b/Main.py index b8d366be..69e95ec8 100644 --- a/Main.py +++ b/Main.py @@ -26,7 +26,7 @@ def main(): parser.add_argument('-b', '--kg', help='build type: "partial", "full", or "post-closure"', required=True) parser.add_argument('-r', '--rel', help='yes/no - adding inverse relations to knowledge graph', required=True) parser.add_argument('-s', '--owl', help='yes/no - removing OWL Semantics from knowledge graph', required=True) - parser.add_argument('-m', '--nde', help='yes/no - adding node metadata to knowledge graph', required=True) + parser.add_argument('-m', '--mta', help='yes/no - adding entity metadata to knowledge graph', required=True) parser.add_argument('-o', '--out', help='name/path to directory where to write knowledge graph', required=True) args = parser.parse_args() @@ -85,21 +85,21 @@ def main(): if args.kg == 'partial': kg = PartialBuild(construction=args.app, - node_data=args.nde, + node_data=args.mta, inverse_relations=args.rel, decode_owl=args.owl, cpus=cpus, write_location=args.out) elif args.kg == 'post-closure': kg = PostClosureBuild(construction=args.app, - node_data=args.nde, + node_data=args.mta, inverse_relations=args.rel, decode_owl=args.owl, cpus=cpus, write_location=args.out) else: kg = FullBuild(construction=args.app, - node_data=args.nde, + node_data=args.mta, inverse_relations=args.rel, decode_owl=args.owl, cpus=cpus, diff --git a/README.rst b/README.rst index e7f09f14..599e2b98 100644 --- a/README.rst +++ b/README.rst @@ -129,7 +129,7 @@ The ``pkt_kg`` library requires a specific project directory structure. | | | knowledge_graphs/ | | - | node_data/ + | metadata/ | | | ontologies/ | | @@ -165,7 +165,9 @@ The `KG Construction`_ Wiki page provides a detailed description of the knowledg * `resources/construction_approach/subclass_construction_map.pkl`_ * `resources/Master_Edge_List_Dict.json`_ ➞ *automatically created after edge list construction* -* `resources/node_data/node_metadata_dict.pkl <https://github.com/callahantiff/PheKnowLator/blob/master/resources/node_data/README.md>`__ ➞ *if adding metadata for new edges to the knowledge graph* +* `resources/metadata/entity_metadata_dict.pkl <https://github +.com/callahantiff/PheKnowLator/blob/master/resources/metadata/README.md>`__ ➞ *if adding metadata for new edges to the +knowledge graph* * `resources/knowledge_graphs/PheKnowLator_MergedOntologies*.owl`_ ➞ *see* `ontology README`_ *for information* * `resources/relations_data/RELATIONS_LABELS.txt`_ * `resources/relations_data/INVERSE_RELATIONS.txt`_ ➞ *if including inverse relations* @@ -221,7 +223,7 @@ The program can be run locally using the `main.py`_ script or using the `main.ip kg = PartialBuild(kg_version='v2.0.0', write_location='./resources/knowledge_graphs', construction='subclass, - node_data='yes, + metadata='yes, inverse_relations='yes', cpus=available_cpus, decode_owl='yes') @@ -437,7 +439,8 @@ Callahan TJ, Tripodi IJ, Hunter LE, Baumgartner WA. `A Framework for Automated C .. _`resources/Master_Edge_List_Dict.json`: https://www.dropbox.com/s/t8sgzd847t1rof4/Master_Edge_List_Dict.json?dl=1 -.. _`resources/node_data/node_metadata_dict.pkl`: https://github.com/callahantiff/PheKnowLator/blob/master/resources/node_data/README.md +.. _`resources/metadata/entity_metadata_dict.pkl`: https://github +.com/callahantiff/PheKnowLator/blob/master/resources/metadata/README.md .. _`resources/knowledge_graphs/PheKnowLator_MergedOntologies*.owl`: https://www.dropbox.com/s/75lkod7vzpgjdaq/PheKnowLator_MergedOntologiesGeneID_Normalized_Cleaned.owl?dl=1 diff --git a/main.ipynb b/main.ipynb index 3f944dc9..b35aa737 100644 --- a/main.ipynb +++ b/main.ipynb @@ -14,7 +14,7 @@ "\n", "**Author:** [TJCallahan](https://mail.google.com/mail/u/0/?view=cm&fs=1&tf=1&to=callahantiff@gmail.com) \n", "**GitHub Repository:** [PheKnowLator](https://github.com/callahantiff/PheKnowLator/wiki) \n", - "**Current Release:** **[`v2.0.0`](https://github.com/callahantiff/PheKnowLator/wiki/v2.0.0)**\n", + "**Current Release:** **`v4.0.0`**\n", "\n", "<br>\n", "\n", @@ -35,7 +35,7 @@ "metadata": {}, "source": [ "## Notebook Purpose\n", - "**Wiki Page:** **[`Release v2.0.0`](https://github.com/callahantiff/PheKnowLator/wiki/v2.0.0)**\n", + "**Wiki Page:** **`Release v4.0.0`**\n", "\n", "<br>\n", "\n", @@ -50,7 +50,7 @@ " - [`ontology_source_list.txt`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/ontology_source_list.txt)\n", " - [`edge_source_list.txt`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/edge_source_list.txt) \n", "\n", - "3. Prepare [relations](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#relations-data) and [node metadata](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#node-metadata) files prior to running the scripts. \n", + "3. Prepare [relations](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#relations-data) and [metadata](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#metadata) files prior to running the scripts. \n", "\n", "4. Select a knowledge graph build type (i.e. `full`, `partial`, or `post-closure`) and construction method (i.e. `instance-based` or `subclass-based`). \n", "\n", @@ -295,7 +295,7 @@ "**Wiki Pages:** \n", "- **[`KG-Construction`](https://github.com/callahantiff/PheKnowLator/wiki/KG-Construction)** \n", "- **[`relations-data`](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#relations-data)** \n", - "- **[`node-metadata`](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#node-metadata)** \n", + "- **[`metadata`](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#metadata)** \n", "\n", "**Jupyter Notebooks:** \n", "- [`Data_Preparation.ipynb`](https://github.com/callahantiff/PheKnowLator/blob/master/notebooks/Data_Preparation.ipynb) \n", @@ -307,7 +307,7 @@ "**Assumptions:** \n", "- <u>Construction Approach</u>. If using the `subclass-based` construction approach, please make sure that a `pickled` dictionary mapping each non-ontology data node to an existing ontology class is created and added to the `./resources/knowledge_graph` directory (please see [here](https://github.com/callahantiff/PheKnowLator/tree/master/resources/knowledge_graphs#construction-method) for additional information). \n", "- <u>Relations Data</u>. If inverse relation data is going to be used to build the knowledge graph, that it has been generated and added to the `./resources/relations_data` directory (please see [here](https://github.com/callahantiff/PheKnowLator/blob/master/resources/relations_data/README.md) for additional information). \n", - "- <u>Node Metadata</u>. If node metadata is going to be used to build the knowledge graph, that it has been generated and added to the `./resources/node_metadata` directory (please see [here](https://github.com/callahantiff/PheKnowLator/blob/master/resources/node_data/README.md) for additional information). \n", + "- <u>Entity Metadata</u>. If entity metadata is going to be used to build the knowledge graph, it has been generated and added to the `./resources/metadata` directory (please see [here](https://github.com/callahantiff/PheKnowLator/blob/master/resources/metadata/README.md) for additional information). \n", "- <u>Decoding OWL Semantics</u>. If decoding OWL-Semantics, please make sure to provide a list of owl:Property types to keep is created and added to the `./resources/knowledge_graph` directory (please see [here](https://github.com/callahantiff/PheKnowLator/wiki/OWL-NETS-2.0) for additional information). \n", "\n", "<br>\n", @@ -339,7 +339,7 @@ "\n", "4. Filter OWL Semantics. Filter the knowledge graph with the goal of removing all edges that contain entities that are needed to support owl semantics, but are not biologically meaningful (please see [here](https://github.com/callahantiff/PheKnowLator/wiki/OWL-NETS-2.0) for additional information).\n", "\n", - "5. Save Edge Lists and Node Metadata. Several versions of the knowledge graph are saved, including: the full knowledge graph (`owl` or Networkx MultiDiGraph `pickle`), triple lists (i.e. integer index and identifier labeled edge lists with a dictionary that maps between the integer indices and node identifiers), and a file of metadata (i.e. identifiers, labels, synonyms, and descriptions) for all nodes in the knowledge graph. \n", + "5. Save Edge Lists and Entity Metadata. Several versions of the knowledge graph are saved, including: the full knowledge graph (`owl` or Networkx MultiDiGraph `pickle`), triple lists (i.e. integer index and identifier labeled edge lists with a dictionary that maps between the integer indexes and node identifiers), and a file of metadata (i.e. identifiers, labels, synonyms, and descriptions) for all nodes in the knowledge graph. \n", "\n", "<br>\n", "\n", @@ -359,7 +359,7 @@ "# specify input arguments\n", "build = 'full'\n", "construction_approach = 'subclass'\n", - "add_node_data_to_kg = 'yes'\n", + "add_metadata_to_kg = 'yes'\n", "add_inverse_relations_to_kg = 'yes'\n", "decode_owl_semantics = 'yes'\n", "kg_directory_location = './resources/knowledge_graphs'\n" @@ -374,21 +374,21 @@ "# construct knowledge graphs\n", "if build == 'partial':\n", " kg = PartialBuild(construction=construction_approach,\n", - " node_data=add_node_data_to_kg,\n", + " metadata=add_metadata_to_kg,\n", " inverse_relations=add_inverse_relations_to_kg,\n", " decode_owl=decode_owl_semantics,\n", " cpus=cpus,\n", " write_location=kg_directory_location)\n", "elif build == 'post-closure':\n", " kg = PostClosureBuild(construction=construction_approach,\n", - " node_data=add_node_data_to_kg,\n", + " metadata=add_metadata_to_kg,\n", " inverse_relations=add_inverse_relations_to_kg,\n", " decode_owl=decode_owl_semantics,\n", " cpus=cpus,\n", " write_location=kg_directory_location)\n", "else:\n", " kg = FullBuild(construction=construction_approach,\n", - " node_data=add_node_data_to_kg,\n", + " metadata=add_metadata_to_kg,\n", " inverse_relations=add_inverse_relations_to_kg,\n", " decode_owl=decode_owl_semantics,\n", " cpus=cpus,\n", diff --git a/resources/metadata/README.md b/resources/metadata/README.md new file mode 100644 index 00000000..ba2821e2 --- /dev/null +++ b/resources/metadata/README.md @@ -0,0 +1,42 @@ +*** +## Creating Node and Relation Metadata +*** +*** + +**Wiki Page:** **[`Dependencies`](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#node-metadata)** +**Jupyter Notebook:** **[`Data_Preparation.ipynb`](https://github.com/callahantiff/PheKnowLator/blob/master/notebooks/Data_Preparation.ipynb)** + +___ + +**Purpose:** The knowledge graph can be built with or without the inclusion of node and relation metadata (i.e. labels, descriptions or definitions, and synonyms). If you'd like to create and use node metadata, please see the [`Data_Preparation.ipynb`](https://github.com/callahantiff/PheKnowLator/blob/master/notebooks/Data_Preparation.ipynb) Jupyter Notebook and run the code chunks listed under the **NODE AND RELATION METADATA** section. These code chunks should be run before the knowledge graph is constructed. For more details on what these data sources are and how they are created, please see the `node_data` [`README.md`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/node_data/README.md). + +Example structure of the metadata dictionary is shown below: + +```python +{ + 'nodes': { + 'http://www.ncbi.nlm.nih.gov/gene/1': { + 'Label': 'A1BG', + 'Description': "A1BG has locus group protein-coding' and is located on chromosome 19 (19q13.43).", + 'Synonym': 'HYST2477alpha-1B-glycoprotein|HEL-S-163pA|ABG|A1B|GAB'} ... }, + 'relations': { + 'http://purl.obolibrary.org/obo/RO_0002533': { + 'Label': 'sequence atomic unit', + 'Description': 'Any individual unit of a collection of like units arranged in a linear order', + 'Synonym': 'None'} ... } +} +``` + +<br> + +🛑 *<b>CONSTRAINTS</b>* 🛑 +The algorithm makes the following assumptions: +- If metadata is provided, only those edges with nodes that have metadata will be created; valid edges without metadata will be discarded. +- Metadata for all non-ontology nodes and all relations for edges added to the core set of ontologies will be saved + as a dictionary in the `./resources/metadata/entity_metadata_dict.pkl` repository. + +<br> + +#### Metadata + PheKnowLator +*** +A variety of metadata is pulled from the data sources that are used to support external edges added to enhance the core set of ontologies. For the monthly PheknowLator builds, please see [`pheknowlator_source_metadata.xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/pheknowlator_source_metadata.xlsx) spreadsheet. This spreadsheet has two tabs, one for nodes and one for edges. For each entity (i.e., node or edge) there are several columns, including descriptions of the metadata, the variable type, and even examples of values for eah type of metadata. diff --git a/resources/node_data/README.md b/resources/node_data/README.md deleted file mode 100644 index 84661419..00000000 --- a/resources/node_data/README.md +++ /dev/null @@ -1,54 +0,0 @@ -*** -## Creating Instance Data Node Metadata -*** -*** - -**Wiki Page:** **[`Dependencies`](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#node-metadata)** -**Jupyter Notebook:** **[`Data_Preparation.ipynb`](https://github.com/callahantiff/PheKnowLator/blob/master/notebooks/Data_Preparation.ipynb)** - -___ - -**Purpose:** The knowledge graph can be built with or without the inclusion of node and relation metadata (i.e. -labels, descriptions or definitions, and synonyms). If you'd like to create and use node metadata, please run the -[`Data_Preparation.ipynb`](https://github.com/callahantiff/PheKnowLator/blob/master/notebooks/Data_Preparation.ipynb) Jupyter Notebook and run the code chunks listed under the **INSTANCE AND/OR SUBCLASS (NON-ONTOLOGY CLASS) METADATA** section. These code chunks should be run before the knowledge graph is constructed. For more details on what these data sources are and how they are created, please see the `node_data` [`README.md`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/node_data/README.md). - -Example structure of the metadata dictionary is shown below: - -```python -{ - 'nodes': { - 'http://www.ncbi.nlm.nih.gov/gene/1': { - 'Label': 'A1BG', - 'Description': "A1BG has locus group protein-coding' and is located on chromosome 19 (19q13.43).", - 'Synonym': 'HYST2477alpha-1B-glycoprotein|HEL-S-163pA|ABG|A1B|GAB'} ... }, - 'relations': { - 'http://purl.obolibrary.org/obo/RO_0002533': { - 'Label': 'sequence atomic unit', - 'Description': 'Any individual unit of a collection of like units arranged in a linear order', - 'Synonym': 'None'} ... } -} -``` - -<br> - -🛑 *<b>CONSTRAINTS</b>* 🛑 -The algorithm makes the following assumptions: -- If metadata is provided, only those edges with nodes that have metadata will be created; valid edges without metadata will be discarded. -- Metadata for all non-ontology nodes and all relations for edges added to the core set of ontologies will be saved as a dictionary in the `./resources/node_data/node_metadata_dict.pkl` repository. -- For each identifier we try to obtain the following metadata: `Label`, `Description`, and `Synonym`. An example of these data types is shown below for a [`gene`](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#ncbi-gene) identifier `5620`: - -| **Metadata Type** | **Definition** | **Metadata** | -| :---: | :--- | :--- | -| ID | Node identifiers for instance data sources | `5620` | -| Label | The primary label or name for the node | `LANCL2` | -| Description | A definition or other useful details about the node | `Lanc Like 2` is a `protein-coding` gene that is located on chromosome `7` (map_location: `7p11.2`) | -| Synonym | Alternative terms used for a node | `GPR69B`, `TASP`, `lanC-like protein 2`, `G protein-coupled receptor 69B`, `LanC (bacterial lantibiotic synthetase component C)-like 2`, `LanC lantibiotic synthetase component C-like 2`, `testis-specific adriamycin sensitivity protein` | - -<br> - -#### Metadata + PheKnowLator -*** -The metadata will be used to create the following edges in the knowledge graph: -- **Label** ➞ node `rdfs:label` -- **Description** ➞ node `obo:IAO_0000115` description -- **Synonyms** ➞ node `oboInOwl:hasExactSynonym` synonym diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 8db89c5b..11d5dd7f 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -78,94 +78,94 @@ def test_metadata_processor(self): return None - def test_creates_node_metadata_nodes(self): - """Tests the creates_node_metadata method.""" + def test_creates_entity_metadata_nodes(self): + """Tests the creates_entity_metadata method.""" self.metadata.node_data = [self.metadata.node_data[0].replace('.pkl', '_test.pkl')] self.metadata.extract_metadata(self.graph) # test when the node has metadata - updated_graph_1 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', - 'http://www.ncbi.nlm.nih.gov/gene/2'], - e_type=['entity', 'entity']) + updated_graph_1 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', + 'http://www.ncbi.nlm.nih.gov/gene/2'], + e_type=['entity', 'entity']) self.assertTrue(len(updated_graph_1) == 16) # check that the correct info is returned if only one is an entity - updated_graph_2 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', - 'http://www.ncbi.nlm.nih.gov/gene/2'], - e_type=['entity', 'class']) + updated_graph_2 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', + 'http://www.ncbi.nlm.nih.gov/gene/2'], + e_type=['entity', 'class']) self.assertTrue(len(updated_graph_2) == 8) # check that nothing is returned if the entities are classes - updated_graph_3 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', - 'http://www.ncbi.nlm.nih.gov/gene/2'], - e_type=['class', 'class']) + updated_graph_3 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', + 'http://www.ncbi.nlm.nih.gov/gene/2'], + e_type=['class', 'class']) self.assertTrue(updated_graph_3 is None) # test when the node does not have metadata - updated_graph_4 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', - 'http://www.ncbi.nlm.nih.gov/gene/None'], - e_type=['entity', 'entity']) + updated_graph_4 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', + 'http://www.ncbi.nlm.nih.gov/gene/None'], + e_type=['entity', 'entity']) self.assertTrue(updated_graph_4 is None) # test when node_data is None self.metadata.node_data = None - updated_graph_5 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', - 'http://www.ncbi.nlm.nih.gov/gene/None'], - e_type=['entity', 'entity']) + updated_graph_5 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', + 'http://www.ncbi.nlm.nih.gov/gene/None'], + e_type=['entity', 'entity']) self.assertTrue(updated_graph_5 is None) return None - def test_creates_node_metadata_relations(self): - """Tests the creates_node_metadata method.""" + def test_creates_entity_metadata_relations(self): + """Tests the creates_entity_metadata method.""" self.metadata.node_data = [self.metadata.node_data[0].replace('.pkl', '_test.pkl')] self.metadata.extract_metadata(self.graph) # test when the node has metadata - updated_graph_1 = self.metadata.creates_node_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002310'], - key_type='relations') + updated_graph_1 = self.metadata.creates_entity_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002310'], + key_type='relations') self.assertTrue(len(updated_graph_1) == 2) # check that nothing is returned if the entities are classes - updated_graph_2 = self.metadata.creates_node_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002597'], - e_type=['class'], key_type='relations') + updated_graph_2 = self.metadata.creates_entity_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002597'], + e_type=['class'], key_type='relations') self.assertTrue(len(updated_graph_2) == 2) # test when the node does not have metadata - updated_graph_3 = self.metadata.creates_node_metadata(['http://www.ncbi.nlm.nih.gov/gene/None'], - key_type='relations') + updated_graph_3 = self.metadata.creates_entity_metadata(['http://www.ncbi.nlm.nih.gov/gene/None'], + key_type='relations') self.assertTrue(updated_graph_3 is None) return None - def test_creates_node_metadata_none(self): - """Tests the creates_node_metadata method when node_dict is None.""" + def test_creates_entity_metadata_none(self): + """Tests the creates_entity_metadata method when node_dict is None.""" self.metadata.node_data = [self.metadata.node_data[0].replace('.pkl', '_test.pkl')] self.metadata.extract_metadata(self.graph) self.metadata.node_dict = None # relations -- with valid input - updated_graph_1 = self.metadata.creates_node_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002597'], - key_type='relations') + updated_graph_1 = self.metadata.creates_entity_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002597'], + key_type='relations') self.assertTrue(updated_graph_1 is None) # relations -- without valid input - updated_graph_2 = self.metadata.creates_node_metadata(ent=['http://purl.obolibrary.org/obo/None'], - key_type='relations') + updated_graph_2 = self.metadata.creates_entity_metadata(ent=['http://purl.obolibrary.org/obo/None'], + key_type='relations') self.assertTrue(updated_graph_2 is None) # nodes -- with valid input - updated_graph_3 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', - 'http://www.ncbi.nlm.nih.gov/gene/2'], - e_type=['class', 'class']) + updated_graph_3 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', + 'http://www.ncbi.nlm.nih.gov/gene/2'], + e_type=['class', 'class']) self.assertTrue(updated_graph_3 is None) # nodes -- without valid input - updated_graph_4 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', - 'http://www.ncbi.nlm.nih.gov/gene/None'], - e_type=['entity', 'entity']) + updated_graph_4 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', + 'http://www.ncbi.nlm.nih.gov/gene/None'], + e_type=['entity', 'entity']) self.assertTrue(updated_graph_4 is None) return None From 24a1ac5187d70e2512e5f35ea931ae7aabbd2ee0 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 7 Jan 2022 21:19:50 -0700 Subject: [PATCH 075/112] fixing typo --- resources/metadata/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/metadata/README.md b/resources/metadata/README.md index ba2821e2..70ae7b33 100644 --- a/resources/metadata/README.md +++ b/resources/metadata/README.md @@ -39,4 +39,4 @@ The algorithm makes the following assumptions: #### Metadata + PheKnowLator *** -A variety of metadata is pulled from the data sources that are used to support external edges added to enhance the core set of ontologies. For the monthly PheknowLator builds, please see [`pheknowlator_source_metadata.xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/pheknowlator_source_metadata.xlsx) spreadsheet. This spreadsheet has two tabs, one for nodes and one for edges. For each entity (i.e., node or edge) there are several columns, including descriptions of the metadata, the variable type, and even examples of values for eah type of metadata. +A variety of metadata are pulled from the data sources that are used to support external edges added to enhance the core set of ontologies. For the monthly PheknowLator builds, please see [`pheknowlator_source_metadata.xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/pheknowlator_source_metadata.xlsx) spreadsheet. This spreadsheet has two tabs, one for nodes and one for edges. For each entity (i.e., node or edge) there are several columns, including descriptions of the metadata, the variable type, and even examples of values for eah type of metadata. From 9b9601776b9e803c0f984fb348cdacf35f7537ec Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Wed, 19 Jan 2022 11:53:09 -0500 Subject: [PATCH 076/112] pulling better entity description --- pkt_kg/utils/data_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index 670dd2eb..5ec54913 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -288,9 +288,11 @@ def metadata_api_mapper(nodes: List[str]) -> pd.DataFrame: results = content.query_ids(ids=','.join(request_ids)) if results is not None and (isinstance(results, List) or results['code'] != 404): for row in results: - ids.append(row['stId']); labels.append(row['displayName']); desc.append('None') + ids.append(row['stId']); labels.append(row['displayName']) if row['displayName'] != row['name']: synonyms.append('|'.join(row['name'])) else: synonyms.append('None') + if 'summation' in row.keys(): desc.append('|'.join([x['text'] for x in row['summation']])) + else: desc.append('None') # combine into new data frame metadata = pd.DataFrame(list(zip(ids, labels, desc, synonyms)), columns=['ID', 'Label', 'Description', 'Synonym']) From deb2a433d44e0b1cef9c40df90853a3840692b0d Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Wed, 19 Jan 2022 12:02:59 -0500 Subject: [PATCH 077/112] adding pubmed ids --- pkt_kg/utils/data_utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index 5ec54913..db202a30 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -291,7 +291,12 @@ def metadata_api_mapper(nodes: List[str]) -> pd.DataFrame: ids.append(row['stId']); labels.append(row['displayName']) if row['displayName'] != row['name']: synonyms.append('|'.join(row['name'])) else: synonyms.append('None') - if 'summation' in row.keys(): desc.append('|'.join([x['text'] for x in row['summation']])) + if 'summation' in row.keys(): + definition = '|'.join([x['text'] for x in row['summation']]) + if 'literatureReference' in row.keys(): + lit_ev = '|'.join([x['url'] for x in row['literatureReference'] if 'url' in x.keys()]) + else: lit_ev = '' + desc.append('{} Literature References: {}.'.format(definition, lit_ev)) else: desc.append('None') # combine into new data frame From 12bfa9eb0731738db8ac3bb496268208dca8d3ea Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Thu, 20 Jan 2022 13:36:20 -0500 Subject: [PATCH 078/112] improved explanation --- resources/metadata/README.md | 73 +++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/resources/metadata/README.md b/resources/metadata/README.md index 70ae7b33..471e3a7f 100644 --- a/resources/metadata/README.md +++ b/resources/metadata/README.md @@ -1,42 +1,73 @@ *** -## Creating Node and Relation Metadata +## Preparing Node and Entity Metadata *** *** **Wiki Page:** **[`Dependencies`](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies#node-metadata)** **Jupyter Notebook:** **[`Data_Preparation.ipynb`](https://github.com/callahantiff/PheKnowLator/blob/master/notebooks/Data_Preparation.ipynb)** +**Generated Output:** `./resources/metadata/entity_metadata_dict.pkl` + ___ -**Purpose:** The knowledge graph can be built with or without the inclusion of node and relation metadata (i.e. labels, descriptions or definitions, and synonyms). If you'd like to create and use node metadata, please see the [`Data_Preparation.ipynb`](https://github.com/callahantiff/PheKnowLator/blob/master/notebooks/Data_Preparation.ipynb) Jupyter Notebook and run the code chunks listed under the **NODE AND RELATION METADATA** section. These code chunks should be run before the knowledge graph is constructed. For more details on what these data sources are and how they are created, please see the `node_data` [`README.md`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/node_data/README.md). +A variety of <u>metadata</u> are pulled from the data sources that are used to support external edges added to +enhance the core set of ontologies. For the monthly PheKnowLator builds, please see [`pheknowlator_source_metadata. +xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/pheknowlator_source_metadata.xlsx) spreadsheet. This spreadsheet has two tabs, one for nodes and one for edges. Each entity (i.e., node or relation) there are several columns, including descriptions of the metadata, the variable type, and even examples of values for each type of metadata. -Example structure of the metadata dictionary is shown below: +*Example Metadata Dictionary Output*. The code snippet below is meant to provide a snapshot of how data are organized in the metadata dictionary. As demonstrated by this example, there are three high-level keys: + - `nodes`: Nodes are keyed by CURIE. Every node has a `Label`, `Description`, `Synonym`, and `Dbxref` (whenever possible). Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. + - `edges`: Edges are keyed by a label which represents the edge type (the same label that is used in `resource_info.txt` and `edge_source_list.txt` files. Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. + - `relations`: Relations or `owl:ObjectProperty` objects are keyed by CURIE. Similar to nodes, every relation has a `Label`, `Description`, and `Synonym` (whenever possible). Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. ```python { 'nodes': { - 'http://www.ncbi.nlm.nih.gov/gene/1': { - 'Label': 'A1BG', - 'Description': "A1BG has locus group protein-coding' and is located on chromosome 19 (19q13.43).", - 'Synonym': 'HYST2477alpha-1B-glycoprotein|HEL-S-163pA|ABG|A1B|GAB'} ... }, + 'NCBIGene_2052': { + 'Label': 'EPHX1', + 'Description': "EPHX1 has locus group 'protein-coding' and is located on chromosome 1 (1q42.12).", + 'Synonym': 'epoxide hydrolase 1, microsomal (xenobiotic)|epoxide hydratase|EPHX|HYL1|MEHepoxide hydrolase 1|epoxide hydrolase 1 microsomal|EPOX', + 'Dbxref': 'MIM:132810|HGNC:HGNC:3401|Ensembl:ENSG00000143819', ... }, + 'CHEBI_4592': { + 'Label': 'Dihydroxycarbazepine', + 'Description': "None", + 'Synonym': '10,11-Dihydro-10,11-dihydroxy-5H-dibenzazepine-5-carboxamide|10,11-Dihydroxycarbamazepine', + 'Dbxref': 'CAS:35079-97-1|KEGG:C07495', + 'CTD_chem_gene_ixns.tsv.gz': { + 'ChemicalID': {'MESH:C004822'}, + 'CTD_CasRN': {'35079-97-1'}, + 'CTD_ChemicalName': {'10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine-5-carboxamide'}}, ... }, ... }, + 'edges': { + 'chemical-gene': { + 'CHEBI_4592-NCBIGene_2052': { + {'CTD_chem_gene_ixns.tsv': { + 'CTD_Evidence': [{'CTD_Interaction': '[EPHX1 gene SNP affects the metabolism of carbamazepine epoxide] which affects the chemical synthesis of 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine-5-carboxamide', + 'CTD_InteractionActions': 'affects^chemical synthesis|affects^metabolic processing', + 'CTD_PubMedIDs': '15692831'}]}}, ... }, ... }, ... }, 'relations': { - 'http://purl.obolibrary.org/obo/RO_0002533': { - 'Label': 'sequence atomic unit', - 'Description': 'Any individual unit of a collection of like units arranged in a linear order', - 'Synonym': 'None'} ... } -} -``` + 'RO_0002434': { + 'Label': 'interacts with', + 'Description': 'A relationship that holds between two entities in which the processes executed by the two entities are causally connected.', + 'Synonym': 'in pairwise interaction with'}, ... } +} +``` <br> -🛑 *<b>CONSTRAINTS</b>* 🛑 -The algorithm makes the following assumptions: -- If metadata is provided, only those edges with nodes that have metadata will be created; valid edges without metadata will be discarded. -- Metadata for all non-ontology nodes and all relations for edges added to the core set of ontologies will be saved - as a dictionary in the `./resources/metadata/entity_metadata_dict.pkl` repository. +**Purpose:** +The knowledge graph can be built with or without the inclusion of node and relation metadata (i.e. +labels, descriptions or definitions, and synonyms). If you'd like to create and use node metadata, please run the +[`Data_Preparation.ipynb`](https://github.com/callahantiff/PheKnowLator/blob/master/notebooks/Data_Preparation.ipynb) +Jupyter Notebook and run the code chunks listed under the **NODE AND RELATION METADATA** section. These code chunks should be run before the knowledge graph is constructed. For more details on what these data sources are and how they are created, please see the `metatadata` [`README.md`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/node_data/README.md). <br> -#### Metadata + PheKnowLator -*** -A variety of metadata are pulled from the data sources that are used to support external edges added to enhance the core set of ontologies. For the monthly PheknowLator builds, please see [`pheknowlator_source_metadata.xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/pheknowlator_source_metadata.xlsx) spreadsheet. This spreadsheet has two tabs, one for nodes and one for edges. For each entity (i.e., node or edge) there are several columns, including descriptions of the metadata, the variable type, and even examples of values for eah type of metadata. +🛑 *<b>CONSTRAINTS</b>* 🛑 +The algorithm makes the following assumptions: +- If metadata is provided, only those edges with nodes that have metadata will be created; valid edges without metadata will be discarded. +- Metadata will be divided into `nodes`, `relations`, and `edges`. For `nodes` and `relations`, entities will be + keyed by CURIE. For `edges`, entities will be keyed by their edge type (i.e., the same label that is used in + `resource_info.txt` and `edge_source_list.txt` files). +- For each `node` and `node` entity identifier we try to obtain at least the following metadata: `Label`, + `Description`, and `Synonym`. +- Metadata that are obtained from specific sources that are not ontologies will be added as a nested dictionary that is + keyed by the filename. From a157da22a9ace271e805fe67ea07b30b3d4bca81 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 24 Jan 2022 21:52:21 -0500 Subject: [PATCH 079/112] fixed identifier --- resources/metadata/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/metadata/README.md b/resources/metadata/README.md index 471e3a7f..91e200d6 100644 --- a/resources/metadata/README.md +++ b/resources/metadata/README.md @@ -33,7 +33,7 @@ xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/phekno 'Synonym': '10,11-Dihydro-10,11-dihydroxy-5H-dibenzazepine-5-carboxamide|10,11-Dihydroxycarbamazepine', 'Dbxref': 'CAS:35079-97-1|KEGG:C07495', 'CTD_chem_gene_ixns.tsv.gz': { - 'ChemicalID': {'MESH:C004822'}, + 'CTD_ChemicalID': {'MESH:C004822'}, 'CTD_CasRN': {'35079-97-1'}, 'CTD_ChemicalName': {'10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine-5-carboxamide'}}, ... }, ... }, 'edges': { From 3722191d067b0b3a7c6113f0cf002e4679504405 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 24 Jan 2022 21:52:29 -0500 Subject: [PATCH 080/112] complete overhaul and update --- notebooks/Data_Preparation.ipynb | 8335 +++++++++++++++++++++++++----- 1 file changed, 7053 insertions(+), 1282 deletions(-) diff --git a/notebooks/Data_Preparation.ipynb b/notebooks/Data_Preparation.ipynb index 4da31e74..ec591b6d 100644 --- a/notebooks/Data_Preparation.ipynb +++ b/notebooks/Data_Preparation.ipynb @@ -16,7 +16,7 @@ "\n", "**Author:** [TJCallahan](https://mail.google.com/mail/u/0/?view=cm&fs=1&tf=1&to=callahantiff@gmail.com) \n", "**GitHub Repository:** [PheKnowLator](https://github.com/callahantiff/PheKnowLator/wiki) \n", - "**Release:** **[v2.0.0](https://github.com/callahantiff/PheKnowLator/wiki/v2.0.0)**\n", + "**Release:** **`v4.0.0`**\n", " \n", "<br> \n", " \n", @@ -44,14 +44,15 @@ "## Table of Contents\n", "***\n", "\n", - "### [Create Identifier Maps ](#create-identifier-maps) \n", + "### [Identifier Maps ](#create-identifier-maps) \n", "- [HUMAN TRANSCRIPT, GENE, AND PROTEIN IDENTIFIER MAPPING](#human-transcript,-gene,-and-protein-identifier-mapping)\n", " - [Entrez Gene-Ensembl Transcript](#entrezgene-ensembltranscript) \n", " - [Entrez Gene-Protein Ontology](#entrezgene-proteinontology) \n", " - [Ensembl Gene-Entrez Gene](#ensemblgene-entrezgene)\n", " - [Gene Symbol-Ensembl Transcript](#genesymbol-ensembltranscript) \n", " - [STRING-Protein Ontology](#string-proteinontology) \n", - " - [Uniprot Accession-Protein Ontology](#uniprotaccession-proteinontology)\n", + " - [Uniprot Accession-Protein Ontology](#uniprotaccession-proteinontology) \n", + " - [Uniprot Accession-Entrez Gene](#uniprotaccession-entrezgene)\n", " \n", "\n", "- [OTHER IDENTIFIER MAPPING](#other-identifier-mapping) \n", @@ -62,7 +63,7 @@ " - [Genomic Identifiers - Sequence Ontology](#genomic-soo) \n", "\n", "\n", - "### [Create Edge Datasets](#create-edge-datasets)\n", + "### [Edge Datasets](#create-edge-datasets)\n", "- [ONTOLOGIES](#ontologies) \n", " - [Protein Ontology](#protein-ontology) \n", " - [Relations Ontology](#relations-ontology) \n", @@ -73,12 +74,24 @@ " - [Uniprot Protein-Cofactor and Protein-Catalyst](#uniprot-protein-cofactorcatalyst) \n", "\n", "\n", - "### [Create Instance Data and/or Subclass Metadata](#create-instance-metadata) \n", - "- [Genes/RNA](#gene-and-rna-metadata)\n", - "- [Pathways](#pathway-metadata)\n", - "- [Variants](#variant-metadata) \n", - "- [Relations](#relations-metadata) \n", - "\n", + "### [Node and Relation Metadata](#node-relation-metadata) \n", + "- [CTD_chem_gene_ixns.tsv](#chemical-gene) \n", + "- [CTD_chem_go_enriched.tsv](#chemical-go) \n", + "- [CTD_chemicals_diseases.tsv](#chemical-disease) \n", + "- [CTD_genes_pathways.tsv](#gene-pathway) \n", + "- [goa_human.gaf](#goa) \n", + "- [COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt](#gene-gene) \n", + "- [phenotype.hpoa](#phenotype-disease) \n", + "- [ChEBI2Reactome_All_Levels.txt](#chemical-pathway) \n", + "- [gene_association.reactome](#reactome-goa) \n", + "- [UniProt2Reactome_All_Levels.txt](#uniprot-react) \n", + "- [CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt](#variant-disease) \n", + "- [CLINVAR_VARIANT_GENE_EDGES.txt](#variant-gene) \n", + "- [HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt](#hpa) \n", + "- [UNIPROT_PROTEIN_CATALYST.txt](#uniprot-catalyst) \n", + "- [UNIPROT_PROTEIN_COFACTOR.txt](#uniprot-cofactor) \n", + "- [9606.protein.links.v11.0.txt.gz](#protein-protein) \n", + "- [curated_gene_disease_associations.tsv](#gene-phen) \n", "____" ] }, @@ -159,7 +172,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 160, "metadata": {}, "outputs": [], "source": [ @@ -171,7 +184,7 @@ "relations_data_location = '../resources/relations_data/'\n", "\n", "# directory to write node metadata to\n", - "node_data_location = '../resources/node_data/'\n", + "metadata_location = '../resources/metadata/'\n", "\n", "# directory to write kg construction approach dictionary to\n", "construction_approach_location = '../resources/construction_approach/'\n", @@ -227,6 +240,7 @@ "- [Gene Symbol-Ensembl Transcript](#genesymbol-ensembltranscript) \n", "- [STRING-Protein Ontology](#string-proteinontology) \n", "- [Uniprot Accession-Protein Ontology](#uniprotaccession-proteinontology)\n", + "- [Uniprot Accession-Entrez Gene](#uniprotaccession-entrezgene)\n", "\n", "<br>\n", "\n", @@ -1016,7 +1030,7 @@ "hgnc = hgnc.loc[hgnc['status'].apply(lambda x: x == 'Approved')]\n", "hgnc = hgnc[['hgnc_id', 'entrez_id', 'ensembl_gene_id', 'uniprot_ids', 'symbol', 'locus_type', 'alias_symbol', 'name', 'location', 'alias_name']]\n", "hgnc.rename(columns={'uniprot_ids': 'uniprot_id', 'location': 'map_location', 'locus_type': 'hgnc_gene_type'}, inplace=True)\n", - "hgnc['hgnc_id'].str.replace('.*\\:', '', inplace=True, regex=True) # strip 'HGNC' off of the identifiers\n", + "hgnc['hgnc_id'] = hgnc['hgnc_id'].str.replace('.*\\:', '', regex=True) # strip 'HGNC' off of the identifiers\n", "hgnc.fillna('None', inplace=True) # replace NaN with 'None'\n", "hgnc['entrez_id'] = hgnc['entrez_id'].apply(lambda x: str(int(x)) if x != 'None' else 'None') # make col str\n", "\n", @@ -1030,13 +1044,13 @@ "\n", "# reformat hgnc gene type\n", "for val in genomic_type_mapper['hgnc_gene_type'].keys():\n", - " explode_df_hgnc['hgnc_gene_type'].str.replace(val, genomic_type_mapper['hgnc_gene_type'][val], inplace=True)\n", + " explode_df_hgnc['hgnc_gene_type'] = explode_df_hgnc['hgnc_gene_type'].str.replace(val, genomic_type_mapper['hgnc_gene_type'][val])\n", "\n", "# reformat master hgnc gene type\n", "explode_df_hgnc['master_gene_type'] = explode_df_hgnc['hgnc_gene_type']\n", "master_dict = genomic_type_mapper['hgnc_master_gene_type']\n", "for val in master_dict.keys():\n", - " explode_df_hgnc['master_gene_type'].str.replace(val, master_dict[val], inplace=True)\n", + " explode_df_hgnc['master_gene_type'] = explode_df_hgnc['master_gene_type'].str.replace(val, master_dict[val])\n", "\n", "# post-process reformatted data\n", "explode_df_hgnc.drop(['alias_symbol', 'alias_name'], axis=1, inplace=True) # remove original gene type column\n", @@ -1104,16 +1118,19 @@ "\n", "# reformat ensembl gene type\n", "gene_dict = genomic_type_mapper['ensembl_gene_type']\n", - "for val in gene_dict.keys(): ensembl_geneset['ensembl_gene_type'].str.replace(val, gene_dict[val], inplace=True)\n", + "for val in gene_dict.keys():\n", + " ensembl_geneset['ensembl_gene_type'] = ensembl_geneset['ensembl_gene_type'].str.replace(val, gene_dict[val])\n", "# reformat master gene type\n", "ensembl_geneset['master_gene_type'] = ensembl_geneset['ensembl_gene_type']\n", "gene_dict = genomic_type_mapper['ensembl_master_gene_type']\n", - "for val in gene_dict.keys(): ensembl_geneset['master_gene_type'].str.replace(val, gene_dict[val], inplace=True)\n", + "for val in gene_dict.keys():\n", + " ensembl_geneset['master_gene_type'] = ensembl_geneset['master_gene_type'].str.replace(val, gene_dict[val])\n", "# reformat master transcript type\n", - "ensembl_geneset['ensembl_transcript_type'].str.replace('vault_RNA', 'vaultRNA', inplace=True, regex=False)\n", + "ensembl_geneset['ensembl_transcript_type'] = ensembl_geneset['ensembl_transcript_type'].str.replace('vault_RNA', 'vaultRNA', regex=False)\n", "ensembl_geneset['master_transcript_type'] = ensembl_geneset['ensembl_transcript_type']\n", "trans_dict = genomic_type_mapper['ensembl_master_transcript_type']\n", - "for val in trans_dict.keys(): ensembl_geneset['master_transcript_type'].str.replace(val, trans_dict[val], inplace=True)\n", + "for val in trans_dict.keys():\n", + " ensembl_geneset['master_transcript_type'] = ensembl_geneset['master_transcript_type'].str.replace(val, trans_dict[val])\n", "\n", "# post-process reformatted data\n", "ensembl_geneset.drop_duplicates(subset=None, keep='first', inplace=True)\n", @@ -1326,7 +1343,7 @@ "explode_df_uniprot = explodes_data(explode_df_uniprot.copy(), ['symbol', 'synonyms'], '|')\n", "\n", "# strip out uniprot names\n", - "explode_df_uniprot['transcript_stable_id'].str.replace('\\s.*','', inplace=True, regex=True)\n", + "explode_df_uniprot['transcript_stable_id'] = explode_df_uniprot['transcript_stable_id'].str.replace('\\s.*','', regex=True)\n", "\n", "# remove duplicates\n", "explode_df_uniprot.drop(['Status'], axis=1, inplace=True)\n", @@ -1395,11 +1412,13 @@ "# reformat entrez gene type\n", "explode_df_ncbi_gene['entrez_gene_type'] = explode_df_ncbi_gene['type_of_gene']\n", "gene_dict = genomic_type_mapper['entrez_gene_type']\n", - "for val in gene_dict.keys(): explode_df_ncbi_gene['entrez_gene_type'].str.replace(val, gene_dict[val], inplace=True)\n", + "for val in gene_dict.keys():\n", + " explode_df_ncbi_gene['entrez_gene_type'] = explode_df_ncbi_gene['entrez_gene_type'].str.replace(val, gene_dict[val])\n", "# reformat master gene type\n", "explode_df_ncbi_gene['master_gene_type'] = explode_df_ncbi_gene['entrez_gene_type']\n", "gene_dict = genomic_type_mapper['master_gene_type']\n", - "for val in gene_dict.keys(): explode_df_ncbi_gene['master_gene_type'].str.replace(val, gene_dict[val], inplace=True)\n", + "for val in gene_dict.keys():\n", + " explode_df_ncbi_gene['master_gene_type'] = explode_df_ncbi_gene['master_gene_type'].str.replace(val, gene_dict[val])\n", "\n", "# post-process reformatted data\n", "explode_df_ncbi_gene.drop(['type_of_gene', 'dbXrefs', 'description', 'Nomenclature_status', 'Modification_date',\n", @@ -1453,8 +1472,8 @@ "source": [ "pro_map = pro_map.loc[pro_map['entry'].apply(lambda x: x.startswith('Uni') and '_VAR' not in x and ', ' not in x)] # keep 'UniProtKB' rows\n", "pro_map = pro_map.loc[pro_map['pro_mapping'].apply(lambda x: x.startswith('exact'))] # keep exact mappings\n", - "pro_map['pro_id'].str.replace('PR:','PR_', inplace=True, regex=True) # replace PR: with PR_\n", - "pro_map['entry'].str.replace('(^\\w*\\:)','', inplace=True, regex=True) # remove id prefixes\n", + "pro_map['pro_id'] = pro_map['pro_id'].str.replace('PR:','PR_', regex=True) # replace PR: with PR_\n", + "pro_map['entry'] = pro_map['entry'].str.replace('(^\\w*\\:)','', regex=True) # remove id prefixes\n", "pro_map = pro_map.loc[pro_map['pro_id'].apply(lambda x: '-' not in x)] # remove isoforms\n", "pro_map.rename(columns={'entry': 'uniprot_id'}, inplace=True) # rename columns before merging\n", "pro_map.drop(['pro_mapping'], axis=1, inplace=True) # remove uneeded columns\n", @@ -1604,12 +1623,12 @@ "merged_data.fillna('None', inplace=True)\n", "\n", "# make sure that all gene and transcript type colunmns have none recoded to unknown or not protein-coding\n", - "merged_data['hgnc_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", - "merged_data['ensembl_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", - "merged_data['entrez_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", - "merged_data['master_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", - "merged_data['master_transcript_type'].str.replace('None', 'not protein-coding', inplace=True, regex=False)\n", - "merged_data['ensembl_transcript_type'].str.replace('None', 'unknown', inplace=True, regex=False)\n", + "merged_data['hgnc_gene_type'] = merged_data['hgnc_gene_type'].str.replace('None', 'unknown', regex=False)\n", + "merged_data['ensembl_gene_type'] = merged_data['ensembl_gene_type'].str.replace('None', 'unknown', regex=False)\n", + "merged_data['entrez_gene_type'] = merged_data['entrez_gene_type'].str.replace('None', 'unknown', regex=False)\n", + "merged_data['master_gene_type'] = merged_data['master_gene_type'].str.replace('None', 'unknown', regex=False)\n", + "merged_data['master_transcript_type'] = merged_data['master_transcript_type'].str.replace('None', 'not protein-coding', regex=False)\n", + "merged_data['ensembl_transcript_type'] = merged_data['ensembl_transcript_type'].str.replace('None', 'unknown', regex=False)\n", "\n", "# remove duplicates\n", "merged_data_clean = merged_data.drop_duplicates(subset=None, keep='first')\n", @@ -1719,11 +1738,9 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": { - "code_folding": [ - 0 - ] + "code_folding": [] }, "outputs": [], "source": [ @@ -1739,7 +1756,7 @@ "# for _ in range(0, input_size, max_bytes):\n", "# bytes_in += f_in.read(max_bytes)\n", "\n", - "# # load ickled data\n", + "# # load pickled data\n", "# reformatted_mapped_identifiers = pickle.loads(bytes_in)" ] }, @@ -1781,6 +1798,12 @@ " 'Ensembl_Gene_Type', 'Entrez_Gene_Type',\n", " 'Master_Gene_Type1', 'Master_Gene_Type2'])\n", "\n", + "# add prefix to output edge\n", + "egeg_data['Entrez_Gene_IDs'] = 'NCBIGene_' + egeg_data['Entrez_Gene_IDs'].astype(str)\n", + "\n", + "# write data back to file\n", + "egeg_data.to_csv(processed_data_location + 'ENSEMBL_GENE_ENTREZ_GENE_MAP.txt', header=None, sep='\\t', index=False)\n", + "\n", "print('There are {edge_count} ensembl gene-entrez gene edges'.format(edge_count=len(egeg_data)))\n", "egeg_data.head(n=5)" ] @@ -1821,6 +1844,12 @@ " names=['Ensembl_Transcript_IDs', 'Protein_Ontology_IDs',\n", " 'Ensembl_Transcript_Type', 'Master_Transcript_Type'])\n", "\n", + "# add prefix to output edge\n", + "etpr_data['Ensembl_Transcript_ID_Edge'] = 'ensembl_' + etpr_data['Ensembl_Transcript_IDs'].astype(str)\n", + "\n", + "# write data back to file\n", + "etpr_data.to_csv(processed_data_location + 'ENSEMBL_TRANSCRIPT_PROTEIN_ONTOLOGY_MAP.txt', header=None, sep='\\t', index=False)\n", + "\n", "print('There are {edge_count} ensembl transcript-protein ontology edges'.format(edge_count=len(etpr_data)))\n", "etpr_data.head(n=5)" ] @@ -1862,6 +1891,13 @@ " 'Entrez_Gene_Type', 'Ensembl_Transcript_Type',\n", " 'Master_Gene_Type', 'Master_Transcript_Type'])\n", "\n", + "# add prefix to output edge\n", + "eet_data['Ensembl_Transcript_IDs'] = 'ensembl_' + eet_data['Ensembl_Transcript_IDs'].astype(str)\n", + "eet_data['Entrez_Gene_Edge'] = 'NCBIGene_' + eet_data['Entrez_Gene_IDs'].astype(str)\n", + "\n", + "# write data back to file\n", + "eet_data.to_csv(processed_data_location + 'ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt', header=None, sep='\\t', index=False)\n", + "\n", "print('There are {edge_count} entrez gene identifiers-ensembl transcript edges'.format(edge_count=len(eet_data)))\n", "eet_data.head(n=5)" ] @@ -1904,6 +1940,12 @@ " names=['Gene_IDs', 'Protein_Ontology_IDs',\n", " 'Entrez_Gene_Type', 'Master_Gene_Type'])\n", "\n", + "# add prefix to output edge\n", + "egpr_data['Entrez_Gene_Edge'] = 'NCBIGene_' + egpr_data['Gene_IDs'].astype(str)\n", + "\n", + "# write data back to file\n", + "egpr_data.to_csv(processed_data_location + 'ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt', header=None, sep='\\t', index=False)\n", + "\n", "print('There are {edge_count} entrez gene-protein ontology edges'.format(edge_count=len(egpr_data)))\n", "egpr_data.head(n=5)" ] @@ -1948,6 +1990,12 @@ " 'Gene_Type', 'Ensembl_Transcript_Type',\n", " 'Master_Gene_Type', 'Master_Transcript_Type'])\n", "\n", + "# add prefix to output edge\n", + "set_data['Ensembl_Transcript_IDs'] = 'ensembl_' + set_data['Ensembl_Transcript_IDs'].astype(str)\n", + "\n", + "# write data back to file\n", + "set_data.to_csv(processed_data_location + 'GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt', header=None, sep='\\t', index=False)\n", + "\n", "print('There are {edge_count} gene symbol-ensembl transcript edges'.format(edge_count=len(set_data.drop_duplicates())))\n", "set_data.head(n=5)" ] @@ -1987,6 +2035,12 @@ " header=None, delimiter='\\t', low_memory=False, usecols=[0, 1],\n", " names=['STRING_IDs', 'Protein_Ontology_IDs'])\n", "\n", + "# add prefix to output edge\n", + "stpr_data['STRING_IDs'] = '9606.' + stpr_data['STRING_IDs'].astype(str)\n", + "\n", + "# write data back to file\n", + "stpr_data.to_csv(processed_data_location + 'STRING_PRO_ONTOLOGY_MAP.txt', header=None, sep='\\t', index=False)\n", + "\n", "print('There are {edge_count} string-protein ontology edges'.format(edge_count=len(stpr_data.drop_duplicates())))\n", "stpr_data.head(n=5)" ] @@ -2036,6 +2090,53 @@ "uapr_data.head(n=5)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "### Uniprot Accession-Entrez Gene <a class=\"anchor\" id=\"uniprotaccession-entrezgene\"></a>\n", + "\n", + "**Purpose:** To map Uniprot accession identifiers to Entrez Gene identifiers when creating the following edges: \n", + "- gene-gene \n", + "\n", + "**Output:** `UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "genomic_id_mapper(reformatted_mapped_identifiers,\n", + " processed_data_location + 'UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt',\n", + " 'uniprot_id', 'entrez_id', None, 'master_gene_type', None, 'gene_type_update')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# load data, print the number of rows, and preview it\n", + "uaeg_data = pandas.read_csv(processed_data_location + 'UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt',\n", + " header=None, delimiter='\\t', low_memory=False, usecols=[0, 1, 2, 3],\n", + " names=['Uniprot_Accession_IDs', 'Entrez_Gene_IDs',\n", + " 'master_gene_type', 'gene_type_update'])\n", + "\n", + "# add prefix to output edge\n", + "uaeg_data['Entrez_Gene_IDs'] = 'NCBIGene_' + uaeg_data['Entrez_Gene_IDs'].astype(str)\n", + "\n", + "# write data back to file\n", + "uaeg_data.to_csv(processed_data_location + 'UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt', header=None, sep='\\t', index=False)\n", + "\n", + "print('There are {edge_count} uniprot accession-entrez gene edges'.format(edge_count=len(uaeg_data.drop_duplicates())))\n", + "uaeg_data.head(n=5)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2226,7 +2327,7 @@ "# write resulting mappings\n", "with open(processed_data_location + 'MESH_CHEBI_MAP.txt', 'w') as out:\n", " for pair in mesh_edges:\n", - " out.write(pair[0] + '\\t' + pair[1] + '\\n')" + " out.write(pair[0].replace('_', ':') + '\\t' + pair[1] + '\\n')" ] }, { @@ -2251,9 +2352,11 @@ "\n", "### Disease and Phenotype Identifiers <a class=\"anchor\" id=\"disease-identifiers\"></a>\n", "\n", - "**Data Source Wiki Page:** [DisGeNET](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#disgenet) \n", + "**Data Source Wiki Page:** \n", + "- [DisGeNET](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#disgenet) \n", + "- [MedGen](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#ncbi-medgen) \n", "\n", - "**Purpose:** This script downloads the Human Phenotype Ontology (HPO), the MonDO Disease Ontology (MONDO), and [disease_mappings.tsv](https://www.disgenet.org/static/disgenet_ap1/files/downloads/disease_mappings.tsv.gz) in order to map UMLS identifiers to HPO and MONDO identifiers when creating the following edges: \n", + "**Purpose:** This script downloads the Human Phenotype Ontology (HPO), the MonDO Disease Ontology (MONDO), [disease_mappings.tsv](https://www.disgenet.org/static/disgenet_ap1/files/downloads/disease_mappings.tsv.gz), and [MGCONSO.RRF](https://ftp.ncbi.nlm.nih.gov/pub/medgen/MGCONSO.RRF.gz) in order to map UMLS identifiers to HPO and MONDO identifiers when creating the following edges: \n", "- chemical-disease \n", "- disease-phenotype \n", "- chemical-phenotype \n", @@ -2295,7 +2398,24 @@ "mondo_dict = {str(k).lower().split('/')[-1]: {str(i).split('/')[-1].replace('_', ':') for i in v} for k, v in dbxref_res.items() if 'MONDO' in str(v)}\n", "\n", "# pickle dictionary\n", - "pickle.dump(mondo_dict, open(processed_data_location + 'Mondo_Identifier_Map.pkl', 'wb'), protocol=4)" + "pickle.dump(mondo_dict, open(processed_data_location + 'Mondo_Identifier_Map.pkl', 'wb'), protocol=4)\n", + "\n", + "# convert to pandas DataFrame\n", + "temp_list = []\n", + "for k, v in mondo_dict.items():\n", + " if k.startswith('umls:'): new_k = k.split(':')[-1].upper()\n", + " elif k.startswith('hp:'): new_k = k.upper()\n", + " elif k.startswith('mesh:'): new_k = 'MESH:' + k.split(':')[-1].upper()\n", + " elif k.startswith('orphanet:'): new_k = 'ORPHA:' + k.split(':')[-1].upper()\n", + " elif k.startswith('omimps:'): new_k = 'OMIM:' + k.split(':')[-1].upper()\n", + " else: new_k = k\n", + " for i in v:\n", + " temp_list += [[new_k, i.replace(':', '_')]]\n", + " temp_list += [[i, i.replace(':', '_')]]\n", + "\n", + " # convert to \n", + "mondo_df = pandas.DataFrame({'other_id': [x[0] for x in temp_list],\n", + " 'ontology_id': [x[1] for x in temp_list]})" ] }, { @@ -2328,7 +2448,43 @@ "hp_dict = {str(k).lower().split('/')[-1]: {str(i).split('/')[-1].replace('_', ':') for i in v} for k, v in dbxref_res.items() if 'HP' in str(v)}\n", "\n", "# pickle dictionary\n", - "pickle.dump(hp_dict, open(processed_data_location + 'HPO_Identifier_Map.pkl', 'wb'), protocol=4)" + "pickle.dump(hp_dict, open(processed_data_location + 'HPO_Identifier_Map.pkl', 'wb'), protocol=4)\n", + "\n", + "# convert to pandas DataFrame\n", + "temp_list = []\n", + "for k, v in hp_dict.items():\n", + " if k.startswith('umls:'): new_k = k.split(':')[-1].upper()\n", + " elif k.startswith('mondo:'): new_k = k.upper()\n", + " elif k.startswith('msh:'): new_k = 'MESH:' + k.split(':')[-1].upper()\n", + " elif k.startswith('orpha:'): new_k = 'ORPHA:' + k.split(':')[-1].upper()\n", + " else: new_k = k\n", + " for i in v:\n", + " temp_list += [[new_k, i.replace(':', '_')]]\n", + " temp_list += [[i, i.replace(':', '_')]]\n", + "\n", + "# convert to \n", + "hp_df = pandas.DataFrame({'other_id': [x[0] for x in temp_list],\n", + " 'ontology_id': [x[1] for x in temp_list]})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Combine MONDO and HP Disease Mapping DataFrames into a Single DataFrame*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# combine data frames\n", + "disease_map_df = pandas.concat([mondo_df, hp_df])\n", + "\n", + "# preview data\n", + "disease_map_df.head(n=5)" ] }, { @@ -2356,7 +2512,26 @@ "# reformat data\n", "disease_data['vocabulary'] = disease_data['vocabulary'].str.lower()\n", "disease_data['diseaseId'] = disease_data['diseaseId'].str.lower()\n", - "disease_data['vocabulary'] = ['doid' if x == 'do' else 'ordoid' if x == 'ordo' else x for x in disease_data['vocabulary']]\n", + "disease_data['vocabulary'] = disease_data['vocabulary'].str.replace('hpo', 'HP')\n", + "disease_data['vocabulary'] = disease_data['vocabulary'].str.replace('mondo', 'MONDO')\n", + "disease_data['vocabulary'] = disease_data['vocabulary'].str.replace('msh', 'MESH')\n", + "disease_data['vocabulary'] = disease_data['vocabulary'].str.replace('omim', 'OMIM')\n", + "disease_data['vocabulary'] = disease_data['vocabulary'].str.replace('do', 'doid')\n", + "disease_data['vocabulary'] = disease_data['vocabulary'].str.replace('ordo', 'ORPHA')\n", + "disease_data['vocabulary'] = disease_data['vocabulary'].str.replace('ORPHAid', 'ORPHA')\n", + "\n", + "# capitalize UMLS id\n", + "disease_data['diseaseId'] = disease_data['diseaseId'].str.upper()\n", + "\n", + "# create a disease code column\n", + "disease_data['code'] = disease_data['vocabulary'] + ':' + disease_data['code']\n", + "disease_data['code'] = disease_data['code'].str.replace('HP:HP:', 'HP:')\n", + "\n", + "# rename columns\n", + "disease_data.rename(columns={'diseaseId': 'cui', 'vocabularyName': 'code_name'}, inplace=True)\n", + "\n", + "# remove unneeded columns\n", + "disease_data = disease_data[['cui', 'code', 'code_name', 'vocabulary']].drop_duplicates()\n", "\n", "# preview data\n", "disease_data.head(n=3)" @@ -2366,8 +2541,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "_Build Disease Identifier Dictionary_ \n", - "In order to improve efficiency when mapping different disease terminology identifiers to the [MonDO Disease Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#mondo-disease-ontology) and [Human Phenotype Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#human-phenotype-ontology), we create a dictionary of disease identifiers." + "***\n", + "**MedGen Disease Mappings**" ] }, { @@ -2376,22 +2551,51 @@ "metadata": {}, "outputs": [], "source": [ - "# get all CUIs found with HPO and MONDO\n", - "disease_data_keep = disease_data.query('vocabulary == \"hpo\" | vocabulary == \"mondo\"')\n", + "# download data\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/medgen/MGCONSO.RRF.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'MGCONSO.RRF'):\n", + " data_downloader(url, unprocessed_data_location)\n", + " \n", + "# load data and clean data\n", + "medgen_data = pandas.read_csv(unprocessed_data_location + 'MGCONSO.RRF', header=0, delimiter='|')\n", + "medgen_data = medgen_data[medgen_data['SUPPRESS'] == 'N'].drop_duplicates()\n", + "medgen_data = medgen_data[medgen_data['SAB'].isin(['HPO', 'MONDO', 'MSH', 'ORDO', 'OMIM'])].drop_duplicates()\n", + "\n", + "# reformat codes\n", + "medgen_data['temp_code'] = medgen_data.apply(lambda x: 'MESH:' + x['CODE'] if x['SAB'] == 'MSH'\n", + " else 'OMIM:' + x['CODE'] if x['SAB'] == 'OMIM'\n", + " else 'ORPHA:' + x['SDUI'].split('_')[-1] if x['SAB'] == 'ORDO'\n", + " else x['SDUI'] if x['SAB'] == 'HPO'\n", + " else x['SDUI'] if x['SAB'] == 'MONDO'\n", + " else 'None', axis=1)\n", + "\n", + "# add rows for MedGen identifiers\n", + "temp = medgen_data[['#CUI']]\n", + "temp['temp_code'] = 'MedGen:' + medgen_data['#CUI']\n", + "medgen_data = pandas.concat([medgen_data, temp])\n", + "\n", + "# remove unneeded columns\n", + "medgen_data = medgen_data[['#CUI', 'temp_code', 'STR', 'SAB']].drop_duplicates()\n", + "\n", + "# rename columns\n", + "medgen_data.rename(columns={'#CUI': 'cui',\n", + " 'STR': 'code_name',\n", + " 'temp_code': 'code',\n", + " 'SAB': 'vocabulary'}, inplace=True)\n", "\n", - "# create mondo and hpo dictionary\n", - "hp_mondo_dict = {}\n", - "for idx, row in tqdm(disease_data_keep.iterrows(), total=disease_data_keep.shape[0]):\n", - " if row['vocabulary'] == 'mondo': key, value = 'umls:' + row['diseaseId'], 'MONDO:' + row['code']\n", - " else: key, value = 'umls:' + row['diseaseId'], row['code']\n", - " if key in hp_mondo_dict.keys(): hp_mondo_dict[key] |= {value}\n", - " else: hp_mondo_dict[key] = {value}\n", - "# add ontology mappings from MONDO and HPO\n", - "for key in tqdm(hp_mondo_dict.keys()):\n", - " if key in mondo_dict.keys():\n", - " hp_mondo_dict[key] = set(list(hp_mondo_dict[key]) + list(mondo_dict[key]))\n", - " if key in hp_dict.keys():\n", - " hp_mondo_dict[key] = set(list(hp_mondo_dict[key]) + list(hp_dict[key]))" + "# reformat vocabulary ids\n", + "medgen_data['vocabulary'] = medgen_data['vocabulary'].str.replace('HPO', 'HP')\n", + "medgen_data['vocabulary'] = medgen_data['vocabulary'].str.replace('MSH', 'MESH')\n", + "\n", + "# preview data\n", + "medgen_data.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Combine DisGeNET and MedGen Mappings*" ] }, { @@ -2400,25 +2604,61 @@ "metadata": {}, "outputs": [], "source": [ - "# get all rows for HPO/MONDO CUIs to obtain mappings to other disease identifiers\n", - "disease_data_other = disease_data[disease_data.diseaseId.isin(disease_data_keep['diseaseId'])]\n", + "# combine data\n", + "disease_mapping_data = pandas.concat([disease_data, medgen_data]).drop_duplicates()\n", "\n", - "# get all other codes that map to MONDO or HPO by hopping through MONDO/HPO relevant CUIs\n", - "disease_dict = {}\n", - "for idx, row in tqdm(disease_data_other.iterrows(), total=disease_data_other.shape[0]):\n", - " if row['vocabulary'] == 'mondo' or row['vocabulary'] == 'hpo':\n", - " key, value = 'umls:' + row['diseaseId'].lower(), row['code']\n", - " if key in disease_dict.keys(): disease_dict[key] |= {value}\n", - " else: disease_dict[key] = {value}\n", - " else:\n", - " if 'mondo' not in row['code'] or 'hp' not in row['code']:\n", - " if ':' not in row['code']: key, value = row['vocabulary'] + ':' + row['code'], hp_mondo_dict['umls:' + row['diseaseId']]\n", - " else: key, value = row['code'], hp_mondo_dict['umls:' + row['diseaseId']]\n", - " if key in disease_dict.keys(): disease_dict[key] |= value\n", - " else: disease_dict[key] = value\n", + "# preview data\n", + "disease_mapping_data.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_Build Disease Identifier Dictionary_ \n", + "In order to improve efficiency when mapping different disease terminology identifiers to the [MonDO Disease Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#mondo-disease-ontology) and [Human Phenotype Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#human-phenotype-ontology), we create a dictionary of disease identifiers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# find cuis that map to HP or MONDO\n", + "disease_data_keep = disease_mapping_data.copy()\n", + "disease_data_keep = disease_data_keep.query('vocabulary == \"HP\" | vocabulary == \"MONDO\"')\n", + "disease_data_keep = disease_data_keep[['cui', 'code']]\n", + "cui_list = set(disease_data_keep['cui'])\n", + "\n", + "# obtain a list of other ids that map to the cuis\n", + "temp_df = disease_mapping_data[disease_mapping_data['cui'].isin(cui_list)]\n", + "\n", + "# merge back with original data\n", + "merged_temp = temp_df.merge(disease_data_keep, on='cui')\n", + "merged_temp = merged_temp[['code_x', 'code_y', 'code_name', 'vocabulary']].drop_duplicates()\n", "\n", - "# add ontology dictionaries\n", - "disease_dict = {**disease_dict, **mondo_dict, **hp_dict}" + "# rename the columns\n", + "merged_temp.rename(columns={'code_x': 'cui', 'code_y': 'code'}, inplace=True)\n", + "\n", + "# combine the columns back to main data\n", + "disease_mapping_data = pandas.concat([disease_mapping_data, merged_temp]).drop_duplicates()\n", + "disease_mapping_data = disease_mapping_data[['cui', 'code']].drop_duplicates()\n", + "\n", + "# merge ontology and other mappings together\n", + "cleaned_disease_map = disease_mapping_data.merge(disease_map_df, left_on='cui', right_on='other_id')\n", + "\n", + "# clean up file\n", + "cleaned_disease_map = cleaned_disease_map[['cui', 'ontology_id']]\n", + "cleaned_disease_map.rename(columns={'cui': 'disease_id'}, inplace=True)\n", + "\n", + "# format ontology identifiers\n", + "cleaned_disease_map['ontology_id'] = cleaned_disease_map['ontology_id'].str.replace(':', '_')\n", + "cleaned_disease_map['vocabulary'] = cleaned_disease_map['ontology_id'].str.replace('\\_.*', '', regex=True)\n", + "cleaned_disease_map.drop_duplicates(inplace=True)\n", + "\n", + "# preview data\n", + "cleaned_disease_map.head(n=3)" ] }, { @@ -2431,17 +2671,21 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "code_folding": [] + }, "outputs": [], "source": [ - "with open(processed_data_location + 'DISEASE_MONDO_MAP.txt', 'w') as outfile1, open(processed_data_location + 'PHENOTYPE_HPO_MAP.txt', 'w') as outfile2:\n", - " for k, v in tqdm(disease_dict.items()):\n", - " if any(x for x in v if x.startswith('MONDO')):\n", - " for idx in [x.replace(':', '_') for x in v if 'MONDO' in x]:\n", - " outfile1.write(k.upper().split(':')[-1] + '\\t' + idx + '\\n')\n", - " if any(x for x in v if x.startswith('HP')):\n", - " for idx in [x.replace(':', '_') for x in v if 'HP' in x]:\n", - " outfile2.write(k.upper().split(':')[-1] + '\\t' + idx + '\\n')" + "# split data by ontology and write to file\n", + "mondo_map = cleaned_disease_map[cleaned_disease_map['vocabulary'] == 'MONDO'].drop_duplicates()\n", + "hp_map = cleaned_disease_map[cleaned_disease_map['vocabulary'] == 'HP'].drop_duplicates()\n", + "mondo_map = mondo_map[['disease_id', 'ontology_id']]\n", + "hp_map = hp_map[['disease_id', 'ontology_id']]\n", + "\n", + "\n", + "# write data\n", + "mondo_map.to_csv(processed_data_location + 'DISEASE_MONDO_MAP.txt', header=None, index=False, sep='\\t')\n", + "hp_map.to_csv(processed_data_location + 'PHENOTYPE_HPO_MAP.txt', header=None, index=False, sep='\\t')" ] }, { @@ -2573,7 +2817,7 @@ "# load data\n", "gtex = pandas.read_csv(unprocessed_data_location + 'GTEx_Analysis_2017-06-05_v8_RNASeQCv1.1.9_gene_median_tpm.gct', header=0, skiprows=2, delimiter='\\t')\n", "gtex.fillna('None', inplace=True) # replace NaN with 'None'\n", - "gtex['Name'].str.replace('(\\..*)','', inplace=True, regex=True) # remove identifier type, which appears after '.'\n" + "gtex['Name'] = gtex['Name'].str.replace('(\\..*)','', regex=True) # remove identifier type, which appears after '.'\n" ] }, { @@ -2639,7 +2883,7 @@ "**Create Edge Data Set**\n", "\n", "_Human Protein Atlas_ \n", - "The `HPA` data is looped over and reformatted such that all tissue, cell, cell lines, and fluid types are stored as a nested list. The anatomy type is specified as an item in the list according to its type in order to make mapping more efficient while building the knowledge graph edge list." + "hpaThe `HPA` data is looped over and reformatted such that all tissue, cell, cell lines, and fluid types are stored as a nested list. The anatomy type is specified as an item in the list according to its type in order to make mapping more efficient while building the knowledge graph edge list." ] }, { @@ -2657,10 +2901,10 @@ " if ';' in row_val:\n", " for x in row_val.split(';'):\n", " x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1])\n", - " hpa_results += [ [ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]]\n", + " hpa_results += [ [ens, gene, uni, evid, 'anatomy', 'None', x1, x2, source]]\n", " else:\n", " x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1])\n", - " hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]]\n", + " hpa_results += [[ens, gene, uni, evid, 'anatomy', 'None', x1, x2, source]]\n", " if row['RNA cell line specific nTPM'] != 'None':\n", " row_val = row['RNA cell line specific nTPM']\n", " if ';' in row_val:\n", @@ -2675,10 +2919,10 @@ " if ';' in row_val:\n", " for x in row_val.split(';'):\n", " x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1])\n", - " hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]]\n", + " hpa_results += [[ens, gene, uni, evid, 'anatomy', 'None', x1, x2, source]]\n", " else:\n", " x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1])\n", - " hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]]\n", + " hpa_results += [[ens, gene, uni, evid, 'anatomy', 'None', x1, x2, source]]\n", " if row['RNA blood cell specific nTPM'] != 'None':\n", " row_val = row['RNA blood cell specific nTPM']\n", " if ';' in row_val:\n", @@ -2912,7 +3156,7 @@ " data_downloader(url, unprocessed_data_location)\n", "\n", "# load data\n", - "reactome_pathways2 = pandas.read_csv(unprocessed_data_location + 'gene_association.reactome', header=None, delimiter='\\t', skiprows=3, low_memory=False)" + "reactome_pathways2 = pandas.read_csv(unprocessed_data_location + 'gene_association.reactome', header=None, delimiter='\\t', skiprows=4, low_memory=False)" ] }, { @@ -3219,9 +3463,9 @@ "transcripts = {}\n", "for idx, row in tqdm(transcript_data.iterrows(), total=transcript_data.shape[0]):\n", " if row['transcript_stable_id'] != 'None':\n", - " if row['transcript_stable_id'].str.replace('transcript_stable_id_', '') in transcripts.keys():\n", - " transcripts[row['transcript_stable_id'].str.replace('transcript_stable_id_', '')] += [row['ensembl_transcript_type']]\n", - " else: transcripts[row['transcript_stable_id'].str.replace('transcript_stable_id_', '')] = [row['ensembl_transcript_type']]\n", + " if row['transcript_stable_id'].replace('transcript_stable_id_', '') in transcripts.keys():\n", + " transcripts[row['transcript_stable_id'].replace('transcript_stable_id_', '')] += [row['ensembl_transcript_type']]\n", + " else: transcripts[row['transcript_stable_id'].replace('transcript_stable_id_', '')] = [row['ensembl_transcript_type']]\n", " \n", "# update so map dictionary\n", "for identifier in tqdm(transcripts.keys()):\n", @@ -3446,7 +3690,9 @@ "cell_type": "code", "execution_count": null, "metadata": { - "code_folding": [] + "code_folding": [ + 0 + ] }, "outputs": [], "source": [ @@ -3575,7 +3821,7 @@ "metadata": {}, "outputs": [], "source": [ - "gets_ontology_statistics(ontology_data_location + 'pr_with_imports.owl')" + "gets_ontology_statistics(ontology_data_location + 'pr_with_imports.owl', '../pkt_kg/libs/owltools')" ] }, { @@ -3739,17 +3985,15 @@ "**Data Files:** \n", "Details on each file have been taken from this [README](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/README.txt) and are provided in relevant code chunks below. \n", "##### *Core Data Files* <a class=\"anchor\" id=\"core-data-files\"></a> \n", - "- [`variant_summary.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz) \n", - "- [`submission_summary.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz) \n", - "- [`disease_names`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names)\n", + "- [`variant_summary.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz) \n", "\n", "##### *Metadata Files*<a class=\"anchor\" id=\"metadata-files\"></a> \n", "- [`var_citations.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt) \n", "- [`allele_gene.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz) \n", - "- [`gene_specific_summary.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt) \n", - "- [`gene_condition_source_id`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id) \n", "\n", - "**Output:** `CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt`\n" + "**Output:** \n", + "- `CLINVAR_VARIANT_GENE_EDGES.txt` \n", + "- `CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt`\n" ] }, { @@ -3763,15 +4007,9 @@ "\n", "*Data Files:* \n", "- [`variant_summary.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz) \n", - "- [`submission_summary.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz) \n", - "- [`disease_names`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names)\n", "\n", "*Processing Details* \n", - "<u>Step 1</u>: The first step is down the `variant_summary.txt.gz`, `submission_summary.txt.gz`, and `disease_names` files. After downloading, the files are cleaned to handle missing data, unneeded variables are removed, identifiers and date fields are cleaned and reformatted, and rows without disease/phenotype identifiers are removed (i.e., [`MedGen:CN517202`](https://www.ncbi.nlm.nih.gov/medgen/CN517202)). \n", - "\n", - "<u>Step 2</u>: Merge the `submission_summary`, and `variant_summary` files, back-fill missing information that could not be recovered in the merge, and process and unify submitted and reported disease identifiers.\n", - "\n", - "<u>Step 3</u>: Merge the `submission_summary`, and `disease_names` files to try and recover phenotype entries that were initially submitted as a string, but have no identifier." + "The first step is down the `variant_summary.txt.gz` file. After downloading, the file is cleaned to handle missing data, unneeded variables are removed, identifiers and date fields are cleaned and reformatted, and rows without valid disease/phenotype identifiers are removed. " ] }, { @@ -3830,7 +4068,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 56, "metadata": {}, "outputs": [], "source": [ @@ -3846,14 +4084,14 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "There are 1259766 variant edges\n" + "There are 2266759 variant edges\n" ] }, { @@ -3879,7 +4117,7 @@ " <th></th>\n", " <th>AlleleID</th>\n", " <th>Type</th>\n", - " <th>Name</th>\n", + " <th>VariantName</th>\n", " <th>GeneID</th>\n", " <th>GeneSymbol</th>\n", " <th>HGNC_ID</th>\n", @@ -3888,9 +4126,9 @@ " <th>LastEvaluated</th>\n", " <th>RS# (dbSNP)</th>\n", " <th>...</th>\n", - " <th>Cytogenetic</th>\n", " <th>ReviewStatus</th>\n", " <th>NumberSubmitters</th>\n", + " <th>Guidelines</th>\n", " <th>TestedInGTR</th>\n", " <th>OtherIDs</th>\n", " <th>SubmitterCategories</th>\n", @@ -3911,12 +4149,12 @@ " <td>HGNC:22197</td>\n", " <td>Pathogenic</td>\n", " <td>1</td>\n", - " <td>None</td>\n", + " <td>NaN</td>\n", " <td>397704705</td>\n", " <td>...</td>\n", - " <td>7p22.1</td>\n", " <td>criteria provided, single submitter</td>\n", " <td>2</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>ClinGen:CA215070,OMIM:613653.0001</td>\n", " <td>3</td>\n", @@ -3935,12 +4173,12 @@ " <td>HGNC:22197</td>\n", " <td>Pathogenic</td>\n", " <td>1</td>\n", - " <td>None</td>\n", + " <td>NaN</td>\n", " <td>397704705</td>\n", " <td>...</td>\n", - " <td>7p22.1</td>\n", " <td>criteria provided, single submitter</td>\n", " <td>2</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>ClinGen:CA215070,OMIM:613653.0001</td>\n", " <td>3</td>\n", @@ -3962,9 +4200,9 @@ " <td>June 29, 2010</td>\n", " <td>397704709</td>\n", " <td>...</td>\n", - " <td>7p22.1</td>\n", " <td>no assertion criteria provided</td>\n", " <td>1</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>ClinGen:CA215072,OMIM:613653.0002</td>\n", " <td>1</td>\n", @@ -3986,9 +4224,9 @@ " <td>June 29, 2010</td>\n", " <td>397704709</td>\n", " <td>...</td>\n", - " <td>7p22.1</td>\n", " <td>no assertion criteria provided</td>\n", " <td>1</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>ClinGen:CA215072,OMIM:613653.0002</td>\n", " <td>1</td>\n", @@ -4010,9 +4248,9 @@ " <td>June 29, 2015</td>\n", " <td>150829393</td>\n", " <td>...</td>\n", - " <td>15q25.3</td>\n", " <td>no assertion criteria provided</td>\n", " <td>1</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O...</td>\n", " <td>1</td>\n", @@ -4023,7 +4261,7 @@ " </tr>\n", " </tbody>\n", "</table>\n", - "<p>5 rows × 30 columns</p>\n", + "<p>5 rows × 34 columns</p>\n", "</div>" ], "text/plain": [ @@ -4034,7 +4272,7 @@ "3 15042 Deletion \n", "4 15043 single nucleotide variant \n", "\n", - " Name GeneID GeneSymbol \\\n", + " VariantName GeneID GeneSymbol \\\n", "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", "1 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", "2 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", @@ -4042,76 +4280,71 @@ "4 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", "\n", " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", - "0 HGNC:22197 Pathogenic 1 None \n", - "1 HGNC:22197 Pathogenic 1 None \n", + "0 HGNC:22197 Pathogenic 1 NaN \n", + "1 HGNC:22197 Pathogenic 1 NaN \n", "2 HGNC:22197 Pathogenic 1 June 29, 2010 \n", "3 HGNC:22197 Pathogenic 1 June 29, 2010 \n", "4 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", "\n", - " RS# (dbSNP) ... Cytogenetic ReviewStatus \\\n", - "0 397704705 ... 7p22.1 criteria provided, single submitter \n", - "1 397704705 ... 7p22.1 criteria provided, single submitter \n", - "2 397704709 ... 7p22.1 no assertion criteria provided \n", - "3 397704709 ... 7p22.1 no assertion criteria provided \n", - "4 150829393 ... 15q25.3 no assertion criteria provided \n", - "\n", - " NumberSubmitters TestedInGTR \\\n", - "0 2 N \n", - "1 2 N \n", - "2 1 N \n", - "3 1 N \n", - "4 1 N \n", - "\n", - " OtherIDs SubmitterCategories \\\n", - "0 ClinGen:CA215070,OMIM:613653.0001 3 \n", - "1 ClinGen:CA215070,OMIM:613653.0001 3 \n", - "2 ClinGen:CA215072,OMIM:613653.0002 1 \n", - "3 ClinGen:CA215072,OMIM:613653.0002 1 \n", - "4 ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O... 1 \n", - "\n", - " VariationID PositionVCF ReferenceAlleleVCF AlternateAlleleVCF \n", - "0 2 4820844 GGAT TGCTGTAAACTGTAACTGTAAA \n", - "1 2 4781213 GGAT TGCTGTAAACTGTAACTGTAAA \n", - "2 3 4827360 GCTGCTGGACCTGCC G \n", - "3 3 4787729 GCTGCTGGACCTGCC G \n", - "4 4 85342440 G A \n", - "\n", - "[5 rows x 30 columns]" + " RS# (dbSNP) ... ReviewStatus NumberSubmitters \\\n", + "0 397704705 ... criteria provided, single submitter 2 \n", + "1 397704705 ... criteria provided, single submitter 2 \n", + "2 397704709 ... no assertion criteria provided 1 \n", + "3 397704709 ... no assertion criteria provided 1 \n", + "4 150829393 ... no assertion criteria provided 1 \n", + "\n", + " Guidelines TestedInGTR OtherIDs \\\n", + "0 NaN N ClinGen:CA215070,OMIM:613653.0001 \n", + "1 NaN N ClinGen:CA215070,OMIM:613653.0001 \n", + "2 NaN N ClinGen:CA215072,OMIM:613653.0002 \n", + "3 NaN N ClinGen:CA215072,OMIM:613653.0002 \n", + "4 NaN N ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O... \n", + "\n", + " SubmitterCategories VariationID PositionVCF ReferenceAlleleVCF \\\n", + "0 3 2 4820844 GGAT \n", + "1 3 2 4781213 GGAT \n", + "2 1 3 4827360 GCTGCTGGACCTGCC \n", + "3 1 3 4787729 GCTGCTGGACCTGCC \n", + "4 1 4 85342440 G \n", + "\n", + " AlternateAlleleVCF \n", + "0 TGCTGTAAACTGTAACTGTAAA \n", + "1 TGCTGTAAACTGTAACTGTAAA \n", + "2 G \n", + "3 G \n", + "4 A \n", + "\n", + "[5 rows x 34 columns]" ] }, - "execution_count": 42, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# replace NaN, and \"-\" with 'None'\n", - "var_summary.fillna('None', inplace=True)\n", - "var_summary = var_summary.replace('na', 'None')\n", - "var_summary = var_summary.replace('-', 'None')\n", + "# replace \"na\" and \"-\" with NaN\n", + "var_summary = var_summary.replace('na', numpy.nan)\n", + "var_summary = var_summary.replace('-', numpy.nan)\n", "\n", "# handle ids that are coded as missing (i.e., -1)\n", - "var_summary = var_summary[var_summary['GeneID'] != -1]\n", - "var_summary = var_summary[var_summary['RS# (dbSNP)'] != -1]\n", - "\n", - "# remove rows without an assembly\n", - "var_summary = var_summary[var_summary['Assembly'] != 'None']\n", - "\n", - "# replace cells that contain \";unknown\" with ''\n", - "var_summary = var_summary.replace(';unknown', '')\n", + "var_summary['GeneID'] = var_summary['GeneID'].replace(-1, numpy.nan)\n", + "var_summary['RS# (dbSNP)'] = var_summary['RS# (dbSNP)'].replace(-1, numpy.nan)\n", "\n", "# convert date format\n", "var_summary['LastEvaluated'] = var_summary['LastEvaluated'].str.replace('None', '')\n", "var_summary['LastEvaluated'] = pandas.to_datetime(var_summary['LastEvaluated'])\n", "var_summary['LastEvaluated'] = var_summary['LastEvaluated'].dt.strftime('%B %d, %Y')\n", - "var_summary['LastEvaluated'].fillna('None', inplace=True)\n", + "var_summary['LastEvaluated'] = var_summary['LastEvaluated'].replace('', numpy.nan)\n", "\n", "# rename variables\n", - "var_summary.rename(columns={'#AlleleID': 'AlleleID'}, inplace=True)\n", + "var_summary.rename(columns={'#AlleleID': 'AlleleID',\n", + " 'nsv/esv (dbVar)': 'nsv',\n", + " 'Name': 'VariantName'}, inplace=True)\n", "\n", - "# remove unneeded variables\n", - "drop_list = ['nsv/esv (dbVar)', 'RCVaccession', 'OriginSimple', 'Guidelines']\n", - "var_summary = var_summary.drop(drop_list, axis = 1).drop_duplicates()\n", + "# update variable types\n", + "var_summary['GeneID'] = var_summary['GeneID'].astype('Int64')\n", + "var_summary['RS# (dbSNP)'] = var_summary['RS# (dbSNP)'].astype('Int64')\n", "\n", "# print row count and preview data\n", "print('There are {edge_count} variant edges'.format(edge_count=len(var_summary)))\n", @@ -4127,21 +4360,14 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 58, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 2/2 [07:04<00:00, 212.30s/it]\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - "There are 629684 edges\n" + "There are 1161070 edges\n" ] }, { @@ -4167,7 +4393,7 @@ " <th></th>\n", " <th>AlleleID</th>\n", " <th>Type</th>\n", - " <th>Name</th>\n", + " <th>VariantName</th>\n", " <th>GeneID</th>\n", " <th>GeneSymbol</th>\n", " <th>HGNC_ID</th>\n", @@ -4176,16 +4402,16 @@ " <th>LastEvaluated</th>\n", " <th>RS# (dbSNP)</th>\n", " <th>...</th>\n", - " <th>PhenotypeList</th>\n", " <th>Origin</th>\n", + " <th>OriginSimple</th>\n", " <th>ReviewStatus</th>\n", " <th>NumberSubmitters</th>\n", + " <th>Guidelines</th>\n", " <th>TestedInGTR</th>\n", " <th>OtherIDs</th>\n", " <th>SubmitterCategories</th>\n", " <th>VariationID</th>\n", - " <th>GRCh37_Assembly</th>\n", - " <th>GRCh38_Assembly</th>\n", + " <th>Assembly</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", @@ -4199,22 +4425,22 @@ " <td>HGNC:22197</td>\n", " <td>Pathogenic</td>\n", " <td>1</td>\n", - " <td>None</td>\n", + " <td>NaN</td>\n", " <td>397704705</td>\n", " <td>...</td>\n", - " <td>Spastic paraplegia 48, autosomal recessive</td>\n", " <td>germline;unknown</td>\n", + " <td>germline</td>\n", " <td>criteria provided, single submitter</td>\n", " <td>2</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>ClinGen:CA215070,OMIM:613653.0001</td>\n", " <td>3</td>\n", " <td>2</td>\n", - " <td>{'ChromosomeAccession': 'NC_000007.13', 'Chrom...</td>\n", - " <td>{'ChromosomeAccession': 'NC_000007.14', 'Chrom...</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", " </tr>\n", " <tr>\n", - " <th>1</th>\n", + " <th>2</th>\n", " <td>15042</td>\n", " <td>Deletion</td>\n", " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", @@ -4226,19 +4452,19 @@ " <td>June 29, 2010</td>\n", " <td>397704709</td>\n", " <td>...</td>\n", - " <td>Spastic paraplegia 48, autosomal recessive</td>\n", + " <td>germline</td>\n", " <td>germline</td>\n", " <td>no assertion criteria provided</td>\n", " <td>1</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>ClinGen:CA215072,OMIM:613653.0002</td>\n", " <td>1</td>\n", " <td>3</td>\n", - " <td>{'ChromosomeAccession': 'NC_000007.13', 'Chrom...</td>\n", - " <td>{'ChromosomeAccession': 'NC_000007.14', 'Chrom...</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", " </tr>\n", " <tr>\n", - " <th>2</th>\n", + " <th>4</th>\n", " <td>15043</td>\n", " <td>single nucleotide variant</td>\n", " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", @@ -4250,19 +4476,19 @@ " <td>June 29, 2015</td>\n", " <td>150829393</td>\n", " <td>...</td>\n", - " <td>Galloway-Mowat syndrome 1</td>\n", + " <td>germline</td>\n", " <td>germline</td>\n", " <td>no assertion criteria provided</td>\n", " <td>1</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O...</td>\n", " <td>1</td>\n", " <td>4</td>\n", - " <td>{'ChromosomeAccession': 'NC_000015.9', 'Chromo...</td>\n", - " <td>{'ChromosomeAccession': 'NC_000015.10', 'Chrom...</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", " </tr>\n", " <tr>\n", - " <th>3</th>\n", + " <th>6</th>\n", " <td>15044</td>\n", " <td>single nucleotide variant</td>\n", " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", @@ -4274,19 +4500,19 @@ " <td>December 30, 2019</td>\n", " <td>267606829</td>\n", " <td>...</td>\n", - " <td>not provided|Leigh syndrome|Mitochondrial comp...</td>\n", + " <td>germline</td>\n", " <td>germline</td>\n", " <td>criteria provided, multiple submitters, no con...</td>\n", " <td>3</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>ClinGen:CA113792,OMIM:613622.0001</td>\n", " <td>3</td>\n", " <td>5</td>\n", - " <td>{'ChromosomeAccession': 'NC_000011.9', 'Chromo...</td>\n", - " <td>{'ChromosomeAccession': 'NC_000011.10', 'Chrom...</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", " </tr>\n", " <tr>\n", - " <th>4</th>\n", + " <th>8</th>\n", " <td>15045</td>\n", " <td>single nucleotide variant</td>\n", " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", @@ -4298,122 +4524,109 @@ " <td>October 01, 2010</td>\n", " <td>267606830</td>\n", " <td>...</td>\n", - " <td>Mitochondrial complex 1 deficiency, nuclear ty...</td>\n", + " <td>germline</td>\n", " <td>germline</td>\n", " <td>no assertion criteria provided</td>\n", " <td>1</td>\n", + " <td>NaN</td>\n", " <td>N</td>\n", " <td>UniProtKB:Q96CU9#VAR_064571,OMIM:613622.0002,C...</td>\n", " <td>1</td>\n", " <td>6</td>\n", - " <td>{'ChromosomeAccession': 'NC_000011.9', 'Chromo...</td>\n", - " <td>{'ChromosomeAccession': 'NC_000011.10', 'Chrom...</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", - "<p>5 rows × 21 columns</p>\n", + "<p>5 rows × 24 columns</p>\n", "</div>" ], "text/plain": [ " AlleleID Type \\\n", "0 15041 Indel \n", - "1 15042 Deletion \n", - "2 15043 single nucleotide variant \n", - "3 15044 single nucleotide variant \n", - "4 15045 single nucleotide variant \n", + "2 15042 Deletion \n", + "4 15043 single nucleotide variant \n", + "6 15044 single nucleotide variant \n", + "8 15045 single nucleotide variant \n", "\n", - " Name GeneID GeneSymbol \\\n", + " VariantName GeneID GeneSymbol \\\n", "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", - "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", - "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", - "3 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", - "4 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", + "2 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", + "4 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", + "6 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", + "8 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", "\n", " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", - "0 HGNC:22197 Pathogenic 1 None \n", - "1 HGNC:22197 Pathogenic 1 June 29, 2010 \n", - "2 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", - "3 HGNC:26927 Pathogenic 1 December 30, 2019 \n", - "4 HGNC:26927 Pathogenic 1 October 01, 2010 \n", + "0 HGNC:22197 Pathogenic 1 NaN \n", + "2 HGNC:22197 Pathogenic 1 June 29, 2010 \n", + "4 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", + "6 HGNC:26927 Pathogenic 1 December 30, 2019 \n", + "8 HGNC:26927 Pathogenic 1 October 01, 2010 \n", + "\n", + " RS# (dbSNP) ... Origin OriginSimple \\\n", + "0 397704705 ... germline;unknown germline \n", + "2 397704709 ... germline germline \n", + "4 150829393 ... germline germline \n", + "6 267606829 ... germline germline \n", + "8 267606830 ... germline germline \n", + "\n", + " ReviewStatus NumberSubmitters \\\n", + "0 criteria provided, single submitter 2 \n", + "2 no assertion criteria provided 1 \n", + "4 no assertion criteria provided 1 \n", + "6 criteria provided, multiple submitters, no con... 3 \n", + "8 no assertion criteria provided 1 \n", + "\n", + " Guidelines TestedInGTR OtherIDs \\\n", + "0 NaN N ClinGen:CA215070,OMIM:613653.0001 \n", + "2 NaN N ClinGen:CA215072,OMIM:613653.0002 \n", + "4 NaN N ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O... \n", + "6 NaN N ClinGen:CA113792,OMIM:613622.0001 \n", + "8 NaN N UniProtKB:Q96CU9#VAR_064571,OMIM:613622.0002,C... \n", "\n", - " RS# (dbSNP) ... PhenotypeList \\\n", - "0 397704705 ... Spastic paraplegia 48, autosomal recessive \n", - "1 397704709 ... Spastic paraplegia 48, autosomal recessive \n", - "2 150829393 ... Galloway-Mowat syndrome 1 \n", - "3 267606829 ... not provided|Leigh syndrome|Mitochondrial comp... \n", - "4 267606830 ... Mitochondrial complex 1 deficiency, nuclear ty... \n", - "\n", - " Origin ReviewStatus \\\n", - "0 germline;unknown criteria provided, single submitter \n", - "1 germline no assertion criteria provided \n", - "2 germline no assertion criteria provided \n", - "3 germline criteria provided, multiple submitters, no con... \n", - "4 germline no assertion criteria provided \n", - "\n", - " NumberSubmitters TestedInGTR \\\n", - "0 2 N \n", - "1 1 N \n", - "2 1 N \n", - "3 3 N \n", - "4 1 N \n", - "\n", - " OtherIDs SubmitterCategories \\\n", - "0 ClinGen:CA215070,OMIM:613653.0001 3 \n", - "1 ClinGen:CA215072,OMIM:613653.0002 1 \n", - "2 ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O... 1 \n", - "3 ClinGen:CA113792,OMIM:613622.0001 3 \n", - "4 UniProtKB:Q96CU9#VAR_064571,OMIM:613622.0002,C... 1 \n", - "\n", - " VariationID GRCh37_Assembly \\\n", - "0 2 {'ChromosomeAccession': 'NC_000007.13', 'Chrom... \n", - "1 3 {'ChromosomeAccession': 'NC_000007.13', 'Chrom... \n", - "2 4 {'ChromosomeAccession': 'NC_000015.9', 'Chromo... \n", - "3 5 {'ChromosomeAccession': 'NC_000011.9', 'Chromo... \n", - "4 6 {'ChromosomeAccession': 'NC_000011.9', 'Chromo... \n", - "\n", - " GRCh38_Assembly \n", - "0 {'ChromosomeAccession': 'NC_000007.14', 'Chrom... \n", - "1 {'ChromosomeAccession': 'NC_000007.14', 'Chrom... \n", - "2 {'ChromosomeAccession': 'NC_000015.10', 'Chrom... \n", - "3 {'ChromosomeAccession': 'NC_000011.10', 'Chrom... \n", - "4 {'ChromosomeAccession': 'NC_000011.10', 'Chrom... \n", - "\n", - "[5 rows x 21 columns]" + " SubmitterCategories VariationID \\\n", + "0 3 2 \n", + "2 1 3 \n", + "4 1 4 \n", + "6 3 5 \n", + "8 1 6 \n", + "\n", + " Assembly \n", + "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "4 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "6 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "8 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "\n", + "[5 rows x 24 columns]" ] }, - "execution_count": 43, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# subset df\n", + "var_summary_update_assemb = var_summary.copy()\n", + "var_summary_update_assemb = var_summary_update_assemb[['VariationID', 'Assembly', 'ChromosomeAccession',\n", + " 'Chromosome', 'Start', 'Stop', 'ReferenceAllele',\n", + " 'AlternateAllele', 'Cytogenetic', 'PositionVCF']].drop_duplicates()\n", + "\n", "# identify columns to process\n", "assemb_cols = ['ChromosomeAccession', 'Chromosome', 'Start', 'Stop', 'ReferenceAllele',\n", " 'AlternateAllele','Cytogenetic', 'PositionVCF', 'ReferenceAlleleVCF', 'AlternateAlleleVCF']\n", "\n", - "# process each assembly\n", - "assemblies = set(var_summary['Assembly'])\n", - "for assembly in tqdm(assemblies):\n", - " var_summary[assembly + '_Assembly'] = var_summary.apply(\n", - " lambda x: str({col: x[col] for col in assemb_cols if x[col] != 'None'})\n", - " if x['Assembly'] == assembly else numpy.nan, axis=1)\n", - "\n", - "# drop unneeded columns\n", - "var_summary_update = var_summary.copy()\n", - "var_summary_update = var_summary_update.drop(assemb_cols + ['Assembly'], axis = 1)\n", + "# group data by variant\n", + "df = var_summary_update_assemb.fillna('None')\n", + "df = df.groupby('VariationID').apply(lambda g: str(g.drop(['VariationID'], axis=1).to_dict('records'))).to_dict()\n", "\n", - "# unite columns\n", - "group_cols = [x for x in var_summary_update.columns if not x.endswith('_Assembly')]\n", - "temp1 = var_summary_update[group_cols + ['GRCh37_Assembly']].dropna(subset=['GRCh37_Assembly'])\n", - "temp2 = var_summary_update[group_cols + ['GRCh38_Assembly']].dropna(subset=['GRCh38_Assembly'])\n", - "var_summary_update = temp1.merge(temp2, on=group_cols, how='inner')\n", + "# convert to Pandas DataFrame\n", + "df_items = df.items()\n", + "temp_df = pandas.DataFrame({'VariationID': [x[0] for x in df_items], 'Assembly': [x[1] for x in df_items]})\n", "\n", - "# sort by VariationID and date and keep only the most recent date for each id\n", - "var_summary_update = var_summary_update.sort_values(['VariationID', 'LastEvaluated'], ascending=[True, False])\n", - "var_summary_update = var_summary_update.drop_duplicates(['VariationID'], keep='last')\n", - "\n", - "# replace NaN, and \"-\" with 'None'\n", - "var_summary_update.fillna('None', inplace=True)\n", + "# join temp df with original data\n", + "var_summary_assemb = var_summary.copy().drop(assemb_cols + ['Assembly'], axis = 1)\n", + "var_summary_update = var_summary_assemb.merge(temp_df, on='VariationID', how='left')\n", "\n", "# drop duplicates\n", "var_summary_update.drop_duplicates(inplace=True)\n", @@ -4427,52 +4640,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "<br>\n", - "\n", - "[**`submission_summary.txt.gz`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz)\n", - "\n", - "> Overview of interpretation, phenotypes, observations, and methods reported in each current submission \n", - ">\n", - "> - <u>VariationID</u>: the identifier assigned by ClinVar \n", - "> - <u>ClinicalSignificance</u>: interpretation of the variation-condition relationship \n", - "> - <u>DateLastEvaluated</u>: the last date the variation-condition relationship was evaluated by this submitter \n", - "> - <u>Description</u>: an optional free text description of the basis of the interpretation \n", - "> - <u>SubmittedPhenotypeInfo</u>: the name(s) or identifier(s) submitted for the condition that was interpreted relative to the variant \n", - "> - <u>ReportedPhenotypeInfo</u>: the MedGen identifier/name combinations ClinVar uses to report the condition that was interpreted. 'na' means there is no public identifier in MedGen for the condition. \n", - "> - <u>ReviewStatus</u>: the level of review for this submission \n", - "> - <u>CollectionMethod</u>: the method by which the submitter obtained the information provided \n", - "> - <u>OriginCounts</u>: the reported origin and the number of observations for each origin \n", - "> - <u>Submitter</u>: the submitter of this record \n", - "> - <u>SCV</u>: the accession and current version assigned by ClinVar to the submitted interpretation of the variation-condition relationship \n", - "> - <u>SubmittedGeneSymbol</u>: the symbol provided by the submitter for the gene affected by the variant. May be null. " - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [], - "source": [ - "# download data\n", - "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/submission_summary.txt.gz'\n", - "if not os.path.exists(unprocessed_data_location + 'submission_summary.txt'):\n", - " data_downloader(url, unprocessed_data_location)\n", - "\n", - "# load data\n", - "submission_summary = pandas.read_csv(unprocessed_data_location + 'submission_summary.txt',\n", - " header=0, skiprows=15, delimiter='\\t', low_memory=False)" + "*Process `PhenotypeIDS` and `PhenotypeList` Columns*" ] }, { "cell_type": "code", - "execution_count": 45, - "metadata": {}, + "execution_count": 59, + "metadata": { + "code_folding": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "There are 1120864 edges\n" + "There are 1161070 edges\n" ] }, { @@ -4496,1008 +4678,6164 @@ " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", - " <th>VariationID</th>\n", + " <th>AlleleID</th>\n", + " <th>Type</th>\n", + " <th>VariantName</th>\n", + " <th>GeneID</th>\n", + " <th>GeneSymbol</th>\n", + " <th>HGNC_ID</th>\n", " <th>ClinicalSignificance</th>\n", + " <th>ClinSigSimple</th>\n", " <th>LastEvaluated</th>\n", - " <th>Description</th>\n", - " <th>SubmittedPhenotypeInfo</th>\n", - " <th>ReportedPhenotypeInfo</th>\n", + " <th>RS# (dbSNP)</th>\n", + " <th>...</th>\n", + " <th>OriginSimple</th>\n", " <th>ReviewStatus</th>\n", - " <th>CollectionMethod</th>\n", - " <th>OriginCounts</th>\n", - " <th>Submitter</th>\n", - " <th>GeneSymbol</th>\n", - " <th>ExplanationOfInterpretation</th>\n", + " <th>NumberSubmitters</th>\n", + " <th>Guidelines</th>\n", + " <th>TestedInGTR</th>\n", + " <th>OtherIDs</th>\n", + " <th>SubmitterCategories</th>\n", + " <th>VariationID</th>\n", + " <th>Assembly</th>\n", + " <th>Phenotype</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", - " <td>2</td>\n", - " <td>Pathogenic</td>\n", - " <td>June 29, 2010</td>\n", - " <td>None</td>\n", - " <td>SPASTIC PARAPLEGIA 48, AUTOSOMAL RECESSIVE</td>\n", - " <td>C3150901:Spastic paraplegia 48, autosomal rece...</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>literature only</td>\n", - " <td>germline:na</td>\n", - " <td>OMIM</td>\n", + " <td>15041</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>9907</td>\n", " <td>AP5Z1</td>\n", - " <td>None</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>397704705</td>\n", + " <td>...</td>\n", + " <td>germline</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>2</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", + " <td>3</td>\n", + " <td>2</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", - " <td>3</td>\n", + " <td>15042</td>\n", + " <td>Deletion</td>\n", + " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", " <td>Pathogenic</td>\n", + " <td>1</td>\n", " <td>June 29, 2010</td>\n", - " <td>None</td>\n", - " <td>SPASTIC PARAPLEGIA 48</td>\n", - " <td>C3150901:Spastic paraplegia 48, autosomal rece...</td>\n", + " <td>397704709</td>\n", + " <td>...</td>\n", + " <td>germline</td>\n", " <td>no assertion criteria provided</td>\n", - " <td>literature only</td>\n", - " <td>germline:na</td>\n", - " <td>OMIM</td>\n", - " <td>AP5Z1</td>\n", - " <td>None</td>\n", - " </tr>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", + " <td>1</td>\n", + " <td>3</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", + " </tr>\n", " <tr>\n", - " <th>3</th>\n", - " <td>4</td>\n", + " <th>4</th>\n", + " <td>15043</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", + " <td>9640</td>\n", + " <td>ZNF592</td>\n", + " <td>HGNC:28986</td>\n", " <td>Uncertain significance</td>\n", + " <td>0</td>\n", " <td>June 29, 2015</td>\n", - " <td>None</td>\n", - " <td>RECLASSIFIED - VARIANT OF UNKNOWN SIGNIFICANCE</td>\n", - " <td>C4551772:Galloway-Mowat syndrome 1</td>\n", + " <td>150829393</td>\n", + " <td>...</td>\n", + " <td>germline</td>\n", " <td>no assertion criteria provided</td>\n", - " <td>literature only</td>\n", - " <td>germline:na</td>\n", - " <td>OMIM</td>\n", - " <td>ZNF592</td>\n", - " <td>None</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", + " <td>1</td>\n", + " <td>4</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM...</td>\n", " </tr>\n", " <tr>\n", - " <th>5</th>\n", - " <td>5</td>\n", - " <td>Pathogenic</td>\n", - " <td>October 01, 2010</td>\n", - " <td>None</td>\n", - " <td>MITOCHONDRIAL COMPLEX I DEFICIENCY, NUCLEAR TY...</td>\n", - " <td>C4748791:Mitochondrial complex 1 deficiency, n...</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>literature only</td>\n", - " <td>germline:na</td>\n", - " <td>OMIM</td>\n", + " <th>6</th>\n", + " <td>15044</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", + " <td>55572</td>\n", " <td>FOXRED1</td>\n", - " <td>None</td>\n", + " <td>HGNC:26927</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>December 30, 2019</td>\n", + " <td>267606829</td>\n", + " <td>...</td>\n", + " <td>germline</td>\n", + " <td>criteria provided, multiple submitters, no con...</td>\n", + " <td>3</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA113792|OMIM:613622.0001</td>\n", + " <td>3</td>\n", + " <td>5</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00...</td>\n", " </tr>\n", " <tr>\n", - " <th>4</th>\n", - " <td>5</td>\n", - " <td>Pathogenic</td>\n", - " <td>December 07, 2017</td>\n", - " <td>The Q232X variant in the FOXRED1 gene has been...</td>\n", - " <td>Not Provided</td>\n", - " <td>CN517202:not provided</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>clinical testing</td>\n", - " <td>germline:na</td>\n", - " <td>GeneDx</td>\n", + " <th>8</th>\n", + " <td>15045</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", + " <td>55572</td>\n", " <td>FOXRED1</td>\n", - " <td>None</td>\n", + " <td>HGNC:26927</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>October 01, 2010</td>\n", + " <td>267606830</td>\n", + " <td>...</td>\n", + " <td>germline</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C...</td>\n", + " <td>1</td>\n", + " <td>6</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MedGen:C4748791;MONDO:0032624;OMIM:618241</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", + "<p>5 rows × 23 columns</p>\n", "</div>" ], "text/plain": [ - " VariationID ClinicalSignificance LastEvaluated \\\n", - "0 2 Pathogenic June 29, 2010 \n", - "2 3 Pathogenic June 29, 2010 \n", - "3 4 Uncertain significance June 29, 2015 \n", - "5 5 Pathogenic October 01, 2010 \n", - "4 5 Pathogenic December 07, 2017 \n", - "\n", - " Description \\\n", - "0 None \n", - "2 None \n", - "3 None \n", - "5 None \n", - "4 The Q232X variant in the FOXRED1 gene has been... \n", - "\n", - " SubmittedPhenotypeInfo \\\n", - "0 SPASTIC PARAPLEGIA 48, AUTOSOMAL RECESSIVE \n", - "2 SPASTIC PARAPLEGIA 48 \n", - "3 RECLASSIFIED - VARIANT OF UNKNOWN SIGNIFICANCE \n", - "5 MITOCHONDRIAL COMPLEX I DEFICIENCY, NUCLEAR TY... \n", - "4 Not Provided \n", - "\n", - " ReportedPhenotypeInfo \\\n", - "0 C3150901:Spastic paraplegia 48, autosomal rece... \n", - "2 C3150901:Spastic paraplegia 48, autosomal rece... \n", - "3 C4551772:Galloway-Mowat syndrome 1 \n", - "5 C4748791:Mitochondrial complex 1 deficiency, n... \n", - "4 CN517202:not provided \n", - "\n", - " ReviewStatus CollectionMethod OriginCounts \\\n", - "0 no assertion criteria provided literature only germline:na \n", - "2 no assertion criteria provided literature only germline:na \n", - "3 no assertion criteria provided literature only germline:na \n", - "5 no assertion criteria provided literature only germline:na \n", - "4 criteria provided, single submitter clinical testing germline:na \n", - "\n", - " Submitter GeneSymbol ExplanationOfInterpretation \n", - "0 OMIM AP5Z1 None \n", - "2 OMIM AP5Z1 None \n", - "3 OMIM ZNF592 None \n", - "5 OMIM FOXRED1 None \n", - "4 GeneDx FOXRED1 None " + " AlleleID Type \\\n", + "0 15041 Indel \n", + "2 15042 Deletion \n", + "4 15043 single nucleotide variant \n", + "6 15044 single nucleotide variant \n", + "8 15045 single nucleotide variant \n", + "\n", + " VariantName GeneID GeneSymbol \\\n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", + "2 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", + "4 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", + "6 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", + "8 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", + "\n", + " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", + "0 HGNC:22197 Pathogenic 1 NaN \n", + "2 HGNC:22197 Pathogenic 1 June 29, 2010 \n", + "4 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", + "6 HGNC:26927 Pathogenic 1 December 30, 2019 \n", + "8 HGNC:26927 Pathogenic 1 October 01, 2010 \n", + "\n", + " RS# (dbSNP) ... OriginSimple \\\n", + "0 397704705 ... germline \n", + "2 397704709 ... germline \n", + "4 150829393 ... germline \n", + "6 267606829 ... germline \n", + "8 267606830 ... germline \n", + "\n", + " ReviewStatus NumberSubmitters \\\n", + "0 criteria provided, single submitter 2 \n", + "2 no assertion criteria provided 1 \n", + "4 no assertion criteria provided 1 \n", + "6 criteria provided, multiple submitters, no con... 3 \n", + "8 no assertion criteria provided 1 \n", + "\n", + " Guidelines TestedInGTR OtherIDs \\\n", + "0 NaN N ClinGen:CA215070|OMIM:613653.0001 \n", + "2 NaN N ClinGen:CA215072|OMIM:613653.0002 \n", + "4 NaN N ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... \n", + "6 NaN N ClinGen:CA113792|OMIM:613622.0001 \n", + "8 NaN N UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C... \n", + "\n", + " SubmitterCategories VariationID \\\n", + "0 3 2 \n", + "2 1 3 \n", + "4 1 4 \n", + "6 3 5 \n", + "8 1 6 \n", + "\n", + " Assembly \\\n", + "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "4 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "6 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "8 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "\n", + " Phenotype \n", + "0 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", + "2 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", + "4 MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM... \n", + "6 MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00... \n", + "8 MedGen:C4748791;MONDO:0032624;OMIM:618241 \n", + "\n", + "[5 rows x 23 columns]" ] }, - "execution_count": 45, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# replace NaN and \"-\" with 'None'\n", - "submission_summary.fillna('None', inplace=True)\n", - "submission_summary = submission_summary.replace('na', 'None')\n", - "submission_summary = submission_summary.replace('-', 'None')\n", + "# clean-up identifiers\n", + "var_summary_update['Phenotype'] = var_summary_update['PhenotypeIDS'].str.replace('|', ';').str.replace(',', ';')\n", + "var_summary_update['OtherIDs'] = var_summary_update['OtherIDs'].str.replace(';', '|').str.replace(',', '|')\n", "\n", - "# remove rows without an assembly and without a disease annotation\n", - "submission_summary = submission_summary[submission_summary['SubmittedGeneSymbol'] != 'None']\n", - "\n", - "# convert date format\n", - "submission_summary['DateLastEvaluated'] = submission_summary['DateLastEvaluated'].str.replace('None', '')\n", - "submission_summary['DateLastEvaluated'] = pandas.to_datetime(submission_summary['DateLastEvaluated'])\n", - "submission_summary['DateLastEvaluated'] = submission_summary['DateLastEvaluated'].dt.strftime('%B %d, %Y')\n", - "submission_summary['DateLastEvaluated'].fillna('None', inplace=True)\n", + "# remove unneeded variables\n", + "drop_list = ['PhenotypeList', 'PhenotypeIDS']\n", + "var_summary_update = var_summary_update.drop(drop_list, axis = 1).drop_duplicates()\n", "\n", - "# sort by VariationID and date and keep only the most recent date for each id\n", - "submission_summary = submission_summary.sort_values(['#VariationID', 'DateLastEvaluated'], ascending=[True, False])\n", - "submission_summary = submission_summary.drop_duplicates(['CollectionMethod', '#VariationID'], keep='last')\n", + "# replace NaN with 'None'\n", + "var_summary_update['Phenotype'] = var_summary_update['Phenotype'].fillna('None')\n", "\n", - "# rename variables\n", - "submission_summary.rename(columns={'#VariationID': 'VariationID',\n", - " 'DateLastEvaluated': 'LastEvaluated',\n", - " 'SubmittedGeneSymbol': 'GeneSymbol'}, inplace=True)\n", + "# reformat phenotypeIDS and trim leading whitespace from unnested columns\n", + "var_summary_update['Phenotype'] = var_summary_update['Phenotype'].apply(\n", + " lambda x: ';'.join(set(x for x in ['MONDO:' + i.split(':')[-1] if i.startswith('MONDO')\n", + " else 'HP:' + i.split(':')[-1] if i.startswith('Human Phenotype')\n", + " else 'ORPHA:' + i.split(':')[-1] if i.startswith('Orphanet')\n", + " else 'None' if i.endswith(' conditions')\n", + " else i for i in x.split(';')] if x != 'None')))\n", "\n", - "# remove unneeded variables\n", - "drop_list = ['SCV']\n", - "submission_summary = submission_summary.drop(drop_list, axis = 1).drop_duplicates()\n", + "# drop duplicates\n", + "var_summary_update.drop_duplicates(inplace=True)\n", "\n", "# print row count and preview data\n", - "print('There are {edge_count} edges'.format(edge_count=len(submission_summary)))\n", - "submission_summary.head(n=5)" + "print('There are {edge_count} edges'.format(edge_count=len(var_summary_update)))\n", + "var_summary_update.head(n=5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "*Merge `variant_summary` and `submission_summary` data*\n", + "<br>\n", + "\n", + "#### Metadata Files <a class=\"anchor\" id=\"metadata-files\"></a>\n", + "***\n", + "\n", + "*Data Files:* \n", + "- [`var_citations.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt) \n", + "- [`allele_gene.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz) \n", + "- [`gene_specific_summary.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt) \n", + "\n", + "*Processing Details* \n", + "<u>Step 1</u>: The first step is down the files. After downloading, the files are cleaned to handle missing data, unneeded variables are removed, and identifiers and date fields are cleaned and reformatted. \n", "\n", - "Merge the data on `VariationID`, `GeneSymbol`, `LastEvaluated`, `ReviewStatus`, and `ClinicalSignificance`. Then, back-fill missing information by `VariationID` to recover data that was only available in the `variant_summary` file (i.e., `AlleleID`, `RS# (dbSNP)`, `Type`, `Name`, `GeneID`, `HGNC_ID`, `Origin`) or `submission_summary` file (i.e., `OriginCounts`)." + "<u>Step 2</u>: Merge each cleaned file with the processed variant summary data from the prior steps." ] }, { - "cell_type": "code", - "execution_count": 51, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "# merge files together\n", - "merge_cols = list(set(submission_summary.columns).intersection(set(var_summary_update.columns)))\n", - "var_merged = var_summary_update.merge(submission_summary, on=merge_cols, how='outer')\n", - "\n", - "# reorder columns\n", - "column_order = ['VariationID', 'AlleleID', 'RS# (dbSNP)', 'Type', 'Name',\n", - " 'GeneID', 'HGNC_ID', 'GeneSymbol', 'LastEvaluated',\n", - " 'ReviewStatus', 'Submitter', 'SubmitterCategories',\n", - " 'NumberSubmitters', 'CollectionMethod', 'ClinicalSignificance', \n", - " 'ClinSigSimple','Description', 'SubmittedPhenotypeInfo',\n", - " 'ReportedPhenotypeInfo', 'PhenotypeIDS', 'PhenotypeList', 'OtherIDs',\n", - " 'Origin', 'OriginCounts', 'GRCh37_Assembly', 'GRCh38_Assembly', 'TestedInGTR',\n", - " 'ExplanationOfInterpretation']\n", - "var_merged = var_merged.reindex(columns=column_order)\n", - "\n", - "# sort by VariationID and date and keep only the most recent date for each id\n", - "var_merged = var_merged.sort_values(['VariationID', 'LastEvaluated'], ascending=[True, False]).reset_index(drop=True)\n", - "\n", - "# backfill rows with missing data\n", - "var_merged = var_merged.replace('None', numpy.nan)\n", - "cols = ['AlleleID', 'RS# (dbSNP)', 'Type', 'Name', 'GeneID', 'HGNC_ID', 'Origin', 'OriginCounts']\n", - "var_merged[cols] = var_merged.groupby('VariationID')[cols].ffill().bfill()\n", - "\n", - "# type variables\n", - "var_merged['RS# (dbSNP)'] = pandas.to_numeric(var_merged['RS# (dbSNP)'], downcast='integer', errors='coerce')\n", - "var_merged['AlleleID'] = pandas.to_numeric(var_merged['AlleleID'], downcast='integer', errors='coerce')\n", - "var_merged['GeneID'] = pandas.to_numeric(var_merged['GeneID'], downcast='integer', errors='coerce')\n", + "<br>\n", "\n", - "# replace NaN with 'None'\n", - "var_merged.fillna('None', inplace=True)\n", + "[**`var_citations.txt`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt)\n", "\n", - "# drop duplicates\n", - "var_merged.drop_duplicates(inplace=True)" + "> A tab-delimited report of citations associated with data in ClinVar, connected to the AlleleID, the VariationID, and either rs# from dbSNP or nsv in dbVar.\n", + ">\n", + "> - <u>AlleleID</u>: integer value as stored in the AlleleID field in ClinVar \n", + "> - <u>VariationID</u>: The identifier ClinVar uses to anchor its default display \n", + "> - <u>rs</u>: rs identifier from dbSNP, null if missing \n", + "> - <u>nsv</u>: nsv identifier from dbVar, null if missing \n", + "> - <u>citation_source</u>: The source of the citation, either PubMed, PubMedCentral, or the NCBI Bookshelf \n", + "> - <u>citation_id</u>: The identifier used by that source " ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 60, "metadata": {}, + "outputs": [], "source": [ - "*Process and Align Disease/Phenotype Identifiers*\n", + "# download data\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'var_citations.txt'):\n", + " data_downloader(url, unprocessed_data_location)\n", "\n", - "The two columns from the `submission_summary` file (i.e., `SubmittedPhenotypeInfo`, `ReportedPhenotypeInfo`) and two columns from the `variant_summary` file (i.e., `PhenotypeIDS`, `PhenotypeList`) that contain disease/phenotype identifier information are unnested and processed." + "# load data\n", + "var_citations = pandas.read_csv(unprocessed_data_location + 'var_citations.txt',\n", + " header=0, delimiter='\\t', low_memory=False)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 61, "metadata": {}, "outputs": [ { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "100%|██████████| 4/4 [33:54<00:00, 508.65s/it]\n" + "There are 762226 edges\n" ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>VariationID</th>\n", + " <th>Citation</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>2</td>\n", + " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>3</td>\n", + " <td>PubMed:20613862</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>4</td>\n", + " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>5</td>\n", + " <td>PubMed:30723688|PubMed:25678554|PubMed:20818383</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>6</td>\n", + " <td>PubMed:20818383</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " VariationID Citation\n", + "0 2 PubMed:20613862|PubMed:25741868|PubMedCentral:...\n", + "1 3 PubMed:20613862\n", + "2 4 PubMed:26123727|PubMed:12030328|PubMed:20531441\n", + "3 5 PubMed:30723688|PubMed:25678554|PubMed:20818383\n", + "4 6 PubMed:20818383" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "# unify concept delimiters\n", - "var_merged['PhenotypeList'] = var_merged['PhenotypeList'].str.replace('|', ';')\n", - "var_merged['PhenotypeIDS'] = var_merged['PhenotypeIDS'].str.replace('|', ';').str.replace(',', ';')\n", - "\n", - "# reformat ReportedPhenotypeInfo to match formatting in variant summary\n", - "var_merged['ReportedPhenotypeInfo'] = var_merged['ReportedPhenotypeInfo'].apply(\n", - " lambda x: ';'.join([';'.join(['MedGen:' + i.split(':')[0], i.split(':')[-1]])\n", - " if i.startswith('C') and not i.endswith('not provided')\n", - " else i.split(':')[-1] if i.startswith('na')\n", - " else i for i in x.split(';')]))\n", - "\n", - "# reformat phenotypeIDS and trim leading whitespace from unnested columns\n", - "var_merged['PhenotypeIDS'] = var_merged['PhenotypeIDS'].apply(\n", - " lambda x: ';'.join(['MONDO:' + i.split(':')[-1] if i.startswith('MONDO') \n", - " else 'HP:' + i.split(':')[-1] if i.startswith('Human Phenotype')\n", - " else i for i in x.split(';')]))\n", + "# replace \"na\" and \"-\" with NaN\n", + "var_citations = var_citations.replace('na', numpy.nan)\n", + "var_citations = var_citations.replace('-', numpy.nan)\n", "\n", - "# explode the columns\n", - "cols = ['PhenotypeList', 'PhenotypeIDS', 'SubmittedPhenotypeInfo', 'ReportedPhenotypeInfo']\n", - "for col in tqdm(cols): var_merged = var_merged.assign(**{col: var_merged[col].str.split(';')}).explode(col)\n", - " \n", - "# combine columns and keep only unique concepts\n", - "var_merged['PhenotypeString'] = var_merged['SubmittedPhenotypeInfo'] + ';' + var_merged['PhenotypeList']\n", - "var_merged['PhenotypeString'] = var_merged['PhenotypeString'].apply(lambda x: ';'.join(pandas.unique(x.split(';'))))\n", - "var_merged['PhenotypeID'] = var_merged['ReportedPhenotypeInfo'] + ';' + var_merged['PhenotypeIDS']\n", - "var_merged['PhenotypeID'] = var_merged['PhenotypeID'].apply(lambda x: ';'.join(pandas.unique(x.split(';'))))\n", - "# drop columns that are no longer needed\n", - "drop_list = ['PhenotypeList', 'PhenotypeIDS', 'SubmittedPhenotypeInfo', 'ReportedPhenotypeInfo']\n", - "var_merged = var_merged.drop(drop_list, axis=1).drop_duplicates()\n", - "\n", - "# explode phenotype columns and drop duplicates\n", - "cols = ['PhenotypeString', 'PhenotypeID']\n", - "for col in tqdm(cols): var_merged = var_merged.assign(**{col: var_merged[col].str.split(';')}).explode(col)\n", - "\n", - "# create a single phenotype variable, keep only unique concepts, and drop unneeded columns\n", - "var_merged['Phenotype'] = var_merged['PhenotypeString'] + ';' + var_merged['PhenotypeID']\n", - "var_merged['Phenotype'] = var_merged['Phenotype'].apply(lambda x: ';'.join(pandas.unique(x.split(';'))))\n", - "# drop columns that are no longer needed\n", - "drop_list2 = ['PhenotypeString', 'PhenotypeID']\n", - "var_merged = var_merged.drop(drop_list2, axis=1)\n", - "\n", - "# explode final phenotype column\n", - "var_merged = var_merged.assign(**{'Phenotype': var_merged['Phenotype'].str.split(';')}).explode('Phenotype')\n", - "\n", - "# lower case string phenotypes\n", - "var_merged['Phenotype'] = var_merged['Phenotype'].apply(lambda x: x.lower() if ':' not in x else x)\n", - "\n", - "# replace not provided identifiers with ''\n", - "var_merged['Phenotype'] = var_merged['Phenotype'].str.replace('MedGen:CN517202', 'Not Provided')\n", - "var_merged['Phenotype'] = var_merged['Phenotype'].str.replace('CN517202:not provided', 'Not Provided')\n", - "var_merged['Phenotype'] = var_merged['Phenotype'].str.replace('not provided', 'Not Provided')\n", + "# combine citation information\n", + "var_citations['Citation'] = var_citations['citation_source'] + ':' + var_citations['citation_id']\n", + "# remove unneeded variables\n", + "drop_list = ['citation_source', 'citation_id']\n", + "var_citations = var_citations.drop(drop_list, axis = 1).drop_duplicates()\n", "\n", - "# drop duplicates\n", - "var_merged.drop_duplicates(inplace=True)\n", + "# group data by citations\n", + "var_citations = var_citations.groupby('VariationID').Citation.agg([('Citation', '|'.join)]).reset_index()\n", + "var_citations = var_citations.drop_duplicates().sort_values(by=['VariationID'])\n", "\n", "# print row count and preview data\n", - "print('There are {edge_count} edges'.format(edge_count=len(var_merged)))\n", - "var_merged.head(n=5)" + "print('There are {edge_count} edges'.format(edge_count=len(var_citations)))\n", + "var_citations.head(n=5)" ] }, { - "cell_type": "code", - "execution_count": 47, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['MONDO:MONDO:0013342,MedGen:C3150901,OMIM:613647,Orphanet:306511'] ['Spastic paraplegia 48, autosomal recessive']\n" - ] - } - ], "source": [ - "df = var_summary_update[var_summary_update['VariationID'] == 2]\n", - "print(list(df['PhenotypeIDS']), list(df['PhenotypeList']))\n" + "<br>\n", + "\n", + "[**`allele_gene.txt.gz`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz)\n", + "\n", + "> Reports per ClinVar's AlleleID, the genes that are related to that gene and how they are related.\n", + ">\n", + "> - <u>AlleleID</u>: the integer identifier assigned by ClinVar to each simple allele\n", + "> - <u>GeneID</u>: integer, GeneID in NCBI's Gene database \n", + "> - <u>Symbol</u>: character, Symbol preferred in NCBI's Gene database. Is the symbol from HGNC when available \n", + "> - <u>Name</u>: character, full name of the gene \n", + "> - <u>GenesPerAlleleID</u>: integer, number of genes related to the allele \n", + "> - <u>Category</u>: character, type of allele-gene relationship. The values for category are:\n", + "> - <u>asserted, but not computed</u>: Submitted as related to a gene, but not within the location of that gene on the genome \n", + "> - <u>genes overlapped by variant</u>: The gene and variant overlap \n", + "> - <u>near gene, downstream</u>: Outside the location of the gene on the genome, within 5 kb \n", + "> - <u>near gene, upstream</u>: Outside the location of the gene on the genome, within 5 kb \n", + "> - <u>within multiple genes by overlap</u>: The variant is within genes that overlap on the genome. Includes introns \n", + "> - <u>within single gene</u>: The variant is in only one gene. Includes introns \n", + "> - <u>Source</u>: character, was the relationship submitted or calculated? " ] }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 62, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['C3150901:Spastic paraplegia 48, autosomal recessive'] ['SPASTIC PARAPLEGIA 48, AUTOSOMAL RECESSIVE']\n" - ] - } - ], + "outputs": [], "source": [ - "df = submission_summary[submission_summary['VariationID'] == 2]\n", - "print(list(df['ReportedPhenotypeInfo']), list(df['SubmittedPhenotypeInfo']))" + "# download data\n", + "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'allele_gene.txt'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "\n", + "# load data\n", + "allele_gene = pandas.read_csv(unprocessed_data_location + 'allele_gene.txt',\n", + " header=0, delimiter='\\t', low_memory=False)" ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 63, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "['Not Provided', 'MONDO:0013342', 'spastic paraplegia 48, autosomal recessive', 'MedGen:C3150901', 'OMIM:613647', 'Orphanet:306511', 'spastic paraplegia 48, autosomal recessive', 'MedGen:C3150901', 'Not Provided']\n" + "There are 2328173 edges\n" ] - } - ], - "source": [ - "df = var_merged[var_merged['VariationID'] == 2]\n", - "print(list(df['Phenotype']))\n" + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>AlleleID</th>\n", + " <th>GeneID</th>\n", + " <th>GeneSymbol</th>\n", + " <th>GeneName</th>\n", + " <th>GenesPerAlleleID</th>\n", + " <th>Category</th>\n", + " <th>Source</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>15041</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>15042</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>15043</td>\n", + " <td>9640</td>\n", + " <td>ZNF592</td>\n", + " <td>zinc finger protein 592</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>15044</td>\n", + " <td>55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>FAD dependent oxidoreductase domain containing 1</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>15045</td>\n", + " <td>55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>FAD dependent oxidoreductase domain containing 1</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " AlleleID GeneID GeneSymbol \\\n", + "0 15041 9907 AP5Z1 \n", + "1 15042 9907 AP5Z1 \n", + "2 15043 9640 ZNF592 \n", + "3 15044 55572 FOXRED1 \n", + "4 15045 55572 FOXRED1 \n", + "\n", + " GeneName GenesPerAlleleID \\\n", + "0 adaptor related protein complex 5 subunit zeta 1 1 \n", + "1 adaptor related protein complex 5 subunit zeta 1 1 \n", + "2 zinc finger protein 592 1 \n", + "3 FAD dependent oxidoreductase domain containing 1 1 \n", + "4 FAD dependent oxidoreductase domain containing 1 1 \n", + "\n", + " Category Source \n", + "0 within single gene submitted \n", + "1 within single gene submitted \n", + "2 within single gene submitted \n", + "3 within single gene submitted \n", + "4 within single gene submitted " + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# replace \"na\" and \"-\" with NaN\n", + "allele_gene = allele_gene.replace('na', numpy.nan)\n", + "allele_gene = allele_gene.replace('-', numpy.nan)\n", + "\n", + "# handle gene ids that may be coded as -1\n", + "allele_gene['GeneID'] = allele_gene['GeneID'].replace(-1, numpy.nan)\n", + "\n", + "# rename variables\n", + "allele_gene.rename(columns={'#AlleleID': 'AlleleID',\n", + " 'Symbol': 'GeneSymbol',\n", + " 'Name': 'GeneName'}, inplace=True)\n", + "\n", + "# update variable types\n", + "allele_gene['GeneID'] = allele_gene['GeneID'].astype('Int64')\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(allele_gene)))\n", + "allele_gene.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_Merge and Process Data Sources_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Merge `var_summary_update` with `var_citations` data" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 1161070 edges\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>AlleleID</th>\n", + " <th>Type</th>\n", + " <th>VariantName</th>\n", + " <th>GeneID</th>\n", + " <th>GeneSymbol</th>\n", + " <th>HGNC_ID</th>\n", + " <th>ClinicalSignificance</th>\n", + " <th>ClinSigSimple</th>\n", + " <th>LastEvaluated</th>\n", + " <th>RS# (dbSNP)</th>\n", + " <th>...</th>\n", + " <th>ReviewStatus</th>\n", + " <th>NumberSubmitters</th>\n", + " <th>Guidelines</th>\n", + " <th>TestedInGTR</th>\n", + " <th>OtherIDs</th>\n", + " <th>SubmitterCategories</th>\n", + " <th>VariationID</th>\n", + " <th>Assembly</th>\n", + " <th>Phenotype</th>\n", + " <th>Citation</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>15041</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>397704705</td>\n", + " <td>...</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>2</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", + " <td>3</td>\n", + " <td>2</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", + " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>15042</td>\n", + " <td>Deletion</td>\n", + " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>June 29, 2010</td>\n", + " <td>397704709</td>\n", + " <td>...</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", + " <td>1</td>\n", + " <td>3</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", + " <td>PubMed:20613862</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>15043</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", + " <td>9640</td>\n", + " <td>ZNF592</td>\n", + " <td>HGNC:28986</td>\n", + " <td>Uncertain significance</td>\n", + " <td>0</td>\n", + " <td>June 29, 2015</td>\n", + " <td>150829393</td>\n", + " <td>...</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", + " <td>1</td>\n", + " <td>4</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM...</td>\n", + " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>15044</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", + " <td>55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>HGNC:26927</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>December 30, 2019</td>\n", + " <td>267606829</td>\n", + " <td>...</td>\n", + " <td>criteria provided, multiple submitters, no con...</td>\n", + " <td>3</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>ClinGen:CA113792|OMIM:613622.0001</td>\n", + " <td>3</td>\n", + " <td>5</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00...</td>\n", + " <td>PubMed:30723688|PubMed:25678554|PubMed:20818383</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>15045</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", + " <td>55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>HGNC:26927</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>October 01, 2010</td>\n", + " <td>267606830</td>\n", + " <td>...</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C...</td>\n", + " <td>1</td>\n", + " <td>6</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MedGen:C4748791;MONDO:0032624;OMIM:618241</td>\n", + " <td>PubMed:20818383</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>5 rows × 24 columns</p>\n", + "</div>" + ], + "text/plain": [ + " AlleleID Type \\\n", + "0 15041 Indel \n", + "1 15042 Deletion \n", + "2 15043 single nucleotide variant \n", + "3 15044 single nucleotide variant \n", + "4 15045 single nucleotide variant \n", + "\n", + " VariantName GeneID GeneSymbol \\\n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", + "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", + "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", + "3 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", + "4 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", + "\n", + " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", + "0 HGNC:22197 Pathogenic 1 NaN \n", + "1 HGNC:22197 Pathogenic 1 June 29, 2010 \n", + "2 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", + "3 HGNC:26927 Pathogenic 1 December 30, 2019 \n", + "4 HGNC:26927 Pathogenic 1 October 01, 2010 \n", + "\n", + " RS# (dbSNP) ... ReviewStatus \\\n", + "0 397704705 ... criteria provided, single submitter \n", + "1 397704709 ... no assertion criteria provided \n", + "2 150829393 ... no assertion criteria provided \n", + "3 267606829 ... criteria provided, multiple submitters, no con... \n", + "4 267606830 ... no assertion criteria provided \n", + "\n", + " NumberSubmitters Guidelines TestedInGTR \\\n", + "0 2 NaN N \n", + "1 1 NaN N \n", + "2 1 NaN N \n", + "3 3 NaN N \n", + "4 1 NaN N \n", + "\n", + " OtherIDs SubmitterCategories \\\n", + "0 ClinGen:CA215070|OMIM:613653.0001 3 \n", + "1 ClinGen:CA215072|OMIM:613653.0002 1 \n", + "2 ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... 1 \n", + "3 ClinGen:CA113792|OMIM:613622.0001 3 \n", + "4 UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C... 1 \n", + "\n", + " VariationID Assembly \\\n", + "0 2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "1 3 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "2 4 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "3 5 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "4 6 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "\n", + " Phenotype \\\n", + "0 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", + "1 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", + "2 MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM... \n", + "3 MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00... \n", + "4 MedGen:C4748791;MONDO:0032624;OMIM:618241 \n", + "\n", + " Citation \n", + "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", + "1 PubMed:20613862 \n", + "2 PubMed:26123727|PubMed:12030328|PubMed:20531441 \n", + "3 PubMed:30723688|PubMed:25678554|PubMed:20818383 \n", + "4 PubMed:20818383 \n", + "\n", + "[5 rows x 24 columns]" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# merge data\n", + "merge_cols = list(set(var_summary_update.columns).intersection(set(var_citations.columns)))\n", + "var_summary_merged = var_summary_update.merge(var_citations, on=merge_cols, how='left')\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(var_summary_merged)))\n", + "var_summary_merged.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Merge merged `var_summary_update` with `allele_gene` data" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 1161070 edges\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>AlleleID</th>\n", + " <th>Type</th>\n", + " <th>VariantName</th>\n", + " <th>GeneID</th>\n", + " <th>GeneSymbol</th>\n", + " <th>HGNC_ID</th>\n", + " <th>ClinicalSignificance</th>\n", + " <th>ClinSigSimple</th>\n", + " <th>LastEvaluated</th>\n", + " <th>RS# (dbSNP)</th>\n", + " <th>...</th>\n", + " <th>OtherIDs</th>\n", + " <th>SubmitterCategories</th>\n", + " <th>VariationID</th>\n", + " <th>Assembly</th>\n", + " <th>Phenotype</th>\n", + " <th>Citation</th>\n", + " <th>GeneName</th>\n", + " <th>GenesPerAlleleID</th>\n", + " <th>Category</th>\n", + " <th>Source</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>15041</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>397704705</td>\n", + " <td>...</td>\n", + " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", + " <td>3</td>\n", + " <td>2</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", + " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", + " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>15042</td>\n", + " <td>Deletion</td>\n", + " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", + " <td>9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>HGNC:22197</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>June 29, 2010</td>\n", + " <td>397704709</td>\n", + " <td>...</td>\n", + " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", + " <td>1</td>\n", + " <td>3</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", + " <td>PubMed:20613862</td>\n", + " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>15043</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", + " <td>9640</td>\n", + " <td>ZNF592</td>\n", + " <td>HGNC:28986</td>\n", + " <td>Uncertain significance</td>\n", + " <td>0</td>\n", + " <td>June 29, 2015</td>\n", + " <td>150829393</td>\n", + " <td>...</td>\n", + " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", + " <td>1</td>\n", + " <td>4</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM...</td>\n", + " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", + " <td>zinc finger protein 592</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>15044</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", + " <td>55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>HGNC:26927</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>December 30, 2019</td>\n", + " <td>267606829</td>\n", + " <td>...</td>\n", + " <td>ClinGen:CA113792|OMIM:613622.0001</td>\n", + " <td>3</td>\n", + " <td>5</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00...</td>\n", + " <td>PubMed:30723688|PubMed:25678554|PubMed:20818383</td>\n", + " <td>FAD dependent oxidoreductase domain containing 1</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>15045</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", + " <td>55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>HGNC:26927</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>October 01, 2010</td>\n", + " <td>267606830</td>\n", + " <td>...</td>\n", + " <td>UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C...</td>\n", + " <td>1</td>\n", + " <td>6</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MedGen:C4748791;MONDO:0032624;OMIM:618241</td>\n", + " <td>PubMed:20818383</td>\n", + " <td>FAD dependent oxidoreductase domain containing 1</td>\n", + " <td>1</td>\n", + " <td>within single gene</td>\n", + " <td>submitted</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>5 rows × 28 columns</p>\n", + "</div>" + ], + "text/plain": [ + " AlleleID Type \\\n", + "0 15041 Indel \n", + "1 15042 Deletion \n", + "2 15043 single nucleotide variant \n", + "3 15044 single nucleotide variant \n", + "4 15045 single nucleotide variant \n", + "\n", + " VariantName GeneID GeneSymbol \\\n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", + "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", + "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", + "3 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", + "4 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", + "\n", + " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", + "0 HGNC:22197 Pathogenic 1 NaN \n", + "1 HGNC:22197 Pathogenic 1 June 29, 2010 \n", + "2 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", + "3 HGNC:26927 Pathogenic 1 December 30, 2019 \n", + "4 HGNC:26927 Pathogenic 1 October 01, 2010 \n", + "\n", + " RS# (dbSNP) ... OtherIDs \\\n", + "0 397704705 ... ClinGen:CA215070|OMIM:613653.0001 \n", + "1 397704709 ... ClinGen:CA215072|OMIM:613653.0002 \n", + "2 150829393 ... ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... \n", + "3 267606829 ... ClinGen:CA113792|OMIM:613622.0001 \n", + "4 267606830 ... UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C... \n", + "\n", + " SubmitterCategories VariationID \\\n", + "0 3 2 \n", + "1 1 3 \n", + "2 1 4 \n", + "3 3 5 \n", + "4 1 6 \n", + "\n", + " Assembly \\\n", + "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "1 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "3 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "4 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", + "\n", + " Phenotype \\\n", + "0 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", + "1 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", + "2 MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM... \n", + "3 MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00... \n", + "4 MedGen:C4748791;MONDO:0032624;OMIM:618241 \n", + "\n", + " Citation \\\n", + "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", + "1 PubMed:20613862 \n", + "2 PubMed:26123727|PubMed:12030328|PubMed:20531441 \n", + "3 PubMed:30723688|PubMed:25678554|PubMed:20818383 \n", + "4 PubMed:20818383 \n", + "\n", + " GeneName GenesPerAlleleID \\\n", + "0 adaptor related protein complex 5 subunit zeta 1 1 \n", + "1 adaptor related protein complex 5 subunit zeta 1 1 \n", + "2 zinc finger protein 592 1 \n", + "3 FAD dependent oxidoreductase domain containing 1 1 \n", + "4 FAD dependent oxidoreductase domain containing 1 1 \n", + "\n", + " Category Source \n", + "0 within single gene submitted \n", + "1 within single gene submitted \n", + "2 within single gene submitted \n", + "3 within single gene submitted \n", + "4 within single gene submitted \n", + "\n", + "[5 rows x 28 columns]" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# merge data\n", + "merge_cols = list(set(var_summary_merged.columns).intersection(set(allele_gene.columns)))\n", + "var_summary_merged = var_summary_merged.merge(allele_gene, on=merge_cols, how='left')\n", + "\n", + "# update variable types\n", + "var_summary_merged['GenesPerAlleleID'] = var_summary_merged['GenesPerAlleleID'].astype('Int64')\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(var_summary_merged)))\n", + "var_summary_merged.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Write Edge Lists**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*`variant`-`gene` Edges*" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 1161070 edges\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>VariationID</th>\n", + " <th>AlleleID</th>\n", + " <th>RS# (dbSNP)</th>\n", + " <th>Type</th>\n", + " <th>VariantName</th>\n", + " <th>OtherIDs</th>\n", + " <th>GeneID</th>\n", + " <th>GeneSymbol</th>\n", + " <th>GeneName</th>\n", + " <th>GenesPerAlleleID</th>\n", + " <th>...</th>\n", + " <th>LastEvaluated</th>\n", + " <th>ReviewStatus</th>\n", + " <th>ClinicalSignificance</th>\n", + " <th>ClinSigSimple</th>\n", + " <th>Origin</th>\n", + " <th>OriginSimple</th>\n", + " <th>Source</th>\n", + " <th>SubmitterCategories</th>\n", + " <th>NumberSubmitters</th>\n", + " <th>Citation</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>clinvar_2</td>\n", + " <td>15041</td>\n", + " <td>397704705</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", + " <td>NCBIGene_9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", + " <td>1</td>\n", + " <td>...</td>\n", + " <td>NaN</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>germline;unknown</td>\n", + " <td>germline</td>\n", + " <td>submitted</td>\n", + " <td>3</td>\n", + " <td>2</td>\n", + " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>clinvar_3</td>\n", + " <td>15042</td>\n", + " <td>397704709</td>\n", + " <td>Deletion</td>\n", + " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", + " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", + " <td>NCBIGene_9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", + " <td>1</td>\n", + " <td>...</td>\n", + " <td>June 29, 2010</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>germline</td>\n", + " <td>germline</td>\n", + " <td>submitted</td>\n", + " <td>1</td>\n", + " <td>1</td>\n", + " <td>PubMed:20613862</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>clinvar_4</td>\n", + " <td>15043</td>\n", + " <td>150829393</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", + " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", + " <td>NCBIGene_9640</td>\n", + " <td>ZNF592</td>\n", + " <td>zinc finger protein 592</td>\n", + " <td>1</td>\n", + " <td>...</td>\n", + " <td>June 29, 2015</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>Uncertain significance</td>\n", + " <td>0</td>\n", + " <td>germline</td>\n", + " <td>germline</td>\n", + " <td>submitted</td>\n", + " <td>1</td>\n", + " <td>1</td>\n", + " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>clinvar_5</td>\n", + " <td>15044</td>\n", + " <td>267606829</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", + " <td>ClinGen:CA113792|OMIM:613622.0001</td>\n", + " <td>NCBIGene_55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>FAD dependent oxidoreductase domain containing 1</td>\n", + " <td>1</td>\n", + " <td>...</td>\n", + " <td>December 30, 2019</td>\n", + " <td>criteria provided, multiple submitters, no con...</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>germline</td>\n", + " <td>germline</td>\n", + " <td>submitted</td>\n", + " <td>3</td>\n", + " <td>3</td>\n", + " <td>PubMed:30723688|PubMed:25678554|PubMed:20818383</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>clinvar_6</td>\n", + " <td>15045</td>\n", + " <td>267606830</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", + " <td>UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C...</td>\n", + " <td>NCBIGene_55572</td>\n", + " <td>FOXRED1</td>\n", + " <td>FAD dependent oxidoreductase domain containing 1</td>\n", + " <td>1</td>\n", + " <td>...</td>\n", + " <td>October 01, 2010</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>germline</td>\n", + " <td>germline</td>\n", + " <td>submitted</td>\n", + " <td>1</td>\n", + " <td>1</td>\n", + " <td>PubMed:20818383</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>5 rows × 25 columns</p>\n", + "</div>" + ], + "text/plain": [ + " VariationID AlleleID RS# (dbSNP) Type \\\n", + "0 clinvar_2 15041 397704705 Indel \n", + "1 clinvar_3 15042 397704709 Deletion \n", + "2 clinvar_4 15043 150829393 single nucleotide variant \n", + "3 clinvar_5 15044 267606829 single nucleotide variant \n", + "4 clinvar_6 15045 267606830 single nucleotide variant \n", + "\n", + " VariantName \\\n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... \n", + "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) \n", + "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) \n", + "3 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) \n", + "4 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) \n", + "\n", + " OtherIDs GeneID \\\n", + "0 ClinGen:CA215070|OMIM:613653.0001 NCBIGene_9907 \n", + "1 ClinGen:CA215072|OMIM:613653.0002 NCBIGene_9907 \n", + "2 ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... NCBIGene_9640 \n", + "3 ClinGen:CA113792|OMIM:613622.0001 NCBIGene_55572 \n", + "4 UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C... NCBIGene_55572 \n", + "\n", + " GeneSymbol GeneName \\\n", + "0 AP5Z1 adaptor related protein complex 5 subunit zeta 1 \n", + "1 AP5Z1 adaptor related protein complex 5 subunit zeta 1 \n", + "2 ZNF592 zinc finger protein 592 \n", + "3 FOXRED1 FAD dependent oxidoreductase domain containing 1 \n", + "4 FOXRED1 FAD dependent oxidoreductase domain containing 1 \n", + "\n", + " GenesPerAlleleID ... LastEvaluated \\\n", + "0 1 ... NaN \n", + "1 1 ... June 29, 2010 \n", + "2 1 ... June 29, 2015 \n", + "3 1 ... December 30, 2019 \n", + "4 1 ... October 01, 2010 \n", + "\n", + " ReviewStatus ClinicalSignificance \\\n", + "0 criteria provided, single submitter Pathogenic \n", + "1 no assertion criteria provided Pathogenic \n", + "2 no assertion criteria provided Uncertain significance \n", + "3 criteria provided, multiple submitters, no con... Pathogenic \n", + "4 no assertion criteria provided Pathogenic \n", + "\n", + " ClinSigSimple Origin OriginSimple Source SubmitterCategories \\\n", + "0 1 germline;unknown germline submitted 3 \n", + "1 1 germline germline submitted 1 \n", + "2 0 germline germline submitted 1 \n", + "3 1 germline germline submitted 3 \n", + "4 1 germline germline submitted 1 \n", + "\n", + " NumberSubmitters Citation \n", + "0 2 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", + "1 1 PubMed:20613862 \n", + "2 1 PubMed:26123727|PubMed:12030328|PubMed:20531441 \n", + "3 3 PubMed:30723688|PubMed:25678554|PubMed:20818383 \n", + "4 1 PubMed:20818383 \n", + "\n", + "[5 rows x 25 columns]" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# reduce data set\n", + "var_summary_merged_gene = var_summary_merged.copy()\n", + "var_summary_merged_gene = var_summary_merged_gene[[\n", + " 'VariationID', 'AlleleID', 'RS# (dbSNP)', 'Type', 'VariantName',\n", + " 'OtherIDs', 'GeneID', 'GeneSymbol', 'GeneName', 'GenesPerAlleleID',\n", + " 'Assembly', 'Category', 'Guidelines', 'TestedInGTR', 'RCVaccession', 'LastEvaluated',\n", + " 'ReviewStatus', 'ClinicalSignificance', 'ClinSigSimple', 'Origin', 'OriginSimple', 'Source',\n", + " 'SubmitterCategories', 'NumberSubmitters', 'Citation']]\n", + "var_summary_merged_gene.drop_duplicates(inplace=True)\n", + "\n", + "# head prefix to output\n", + "var_summary_merged_gene['GeneID'] = 'NCBIGene_' + var_summary_merged_gene['GeneID'].astype(str)\n", + "var_summary_merged_gene['VariationID'] = 'clinvar_' + var_summary_merged_gene['VariationID'].astype(str)\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(var_summary_merged_gene)))\n", + "var_summary_merged_gene.head(n=5)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [], + "source": [ + "# write out data\n", + "var_summary_merged_gene.to_csv(open(processed_data_location + 'CLINVAR_VARIANT_GENE_EDGES.txt', 'w'),\n", + " index=False, header=True, sep='\\t')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*`variant`-`disease` / `variant`-`phenotype` Edges*" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 1/1 [00:57<00:00, 57.46s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 4769048 edges\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>VariationID</th>\n", + " <th>AlleleID</th>\n", + " <th>RS# (dbSNP)</th>\n", + " <th>Type</th>\n", + " <th>VariantName</th>\n", + " <th>RCVaccession</th>\n", + " <th>LastEvaluated</th>\n", + " <th>ReviewStatus</th>\n", + " <th>ClinicalSignificance</th>\n", + " <th>ClinSigSimple</th>\n", + " <th>NumberSubmitters</th>\n", + " <th>SubmitterCategories</th>\n", + " <th>Guidelines</th>\n", + " <th>TestedInGTR</th>\n", + " <th>Origin</th>\n", + " <th>OriginSimple</th>\n", + " <th>Assembly</th>\n", + " <th>Phenotype</th>\n", + " <th>Citation</th>\n", + " <th>OtherIDs</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>clinvar_2</td>\n", + " <td>15041</td>\n", + " <td>397704705</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>RCV000000012</td>\n", + " <td>NaN</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>2</td>\n", + " <td>3</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>germline;unknown</td>\n", + " <td>germline</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0013342</td>\n", + " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", + " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", + " </tr>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>clinvar_2</td>\n", + " <td>15041</td>\n", + " <td>397704705</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>RCV000000012</td>\n", + " <td>NaN</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>2</td>\n", + " <td>3</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>germline;unknown</td>\n", + " <td>germline</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>OMIM:613647</td>\n", + " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", + " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", + " </tr>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>clinvar_2</td>\n", + " <td>15041</td>\n", + " <td>397704705</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>RCV000000012</td>\n", + " <td>NaN</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>2</td>\n", + " <td>3</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>germline;unknown</td>\n", + " <td>germline</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>ORPHA:306511</td>\n", + " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", + " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", + " </tr>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>clinvar_2</td>\n", + " <td>15041</td>\n", + " <td>397704705</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>RCV000000012</td>\n", + " <td>NaN</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>2</td>\n", + " <td>3</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>germline;unknown</td>\n", + " <td>germline</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MedGen:C3150901</td>\n", + " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", + " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>clinvar_3</td>\n", + " <td>15042</td>\n", + " <td>397704709</td>\n", + " <td>Deletion</td>\n", + " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", + " <td>RCV000000013</td>\n", + " <td>June 29, 2010</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>1</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>N</td>\n", + " <td>germline</td>\n", + " <td>germline</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0013342</td>\n", + " <td>PubMed:20613862</td>\n", + " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " VariationID AlleleID RS# (dbSNP) Type \\\n", + "0 clinvar_2 15041 397704705 Indel \n", + "0 clinvar_2 15041 397704705 Indel \n", + "0 clinvar_2 15041 397704705 Indel \n", + "0 clinvar_2 15041 397704705 Indel \n", + "1 clinvar_3 15042 397704709 Deletion \n", + "\n", + " VariantName RCVaccession \\\n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... RCV000000012 \n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... RCV000000012 \n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... RCV000000012 \n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... RCV000000012 \n", + "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) RCV000000013 \n", + "\n", + " LastEvaluated ReviewStatus ClinicalSignificance \\\n", + "0 NaN criteria provided, single submitter Pathogenic \n", + "0 NaN criteria provided, single submitter Pathogenic \n", + "0 NaN criteria provided, single submitter Pathogenic \n", + "0 NaN criteria provided, single submitter Pathogenic \n", + "1 June 29, 2010 no assertion criteria provided Pathogenic \n", + "\n", + " ClinSigSimple NumberSubmitters SubmitterCategories Guidelines \\\n", + "0 1 2 3 NaN \n", + "0 1 2 3 NaN \n", + "0 1 2 3 NaN \n", + "0 1 2 3 NaN \n", + "1 1 1 1 NaN \n", + "\n", + " TestedInGTR Origin OriginSimple \\\n", + "0 N germline;unknown germline \n", + "0 N germline;unknown germline \n", + "0 N germline;unknown germline \n", + "0 N germline;unknown germline \n", + "1 N germline germline \n", + "\n", + " Assembly Phenotype \\\n", + "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0013342 \n", + "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... OMIM:613647 \n", + "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... ORPHA:306511 \n", + "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MedGen:C3150901 \n", + "1 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0013342 \n", + "\n", + " Citation \\\n", + "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", + "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", + "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", + "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", + "1 PubMed:20613862 \n", + "\n", + " OtherIDs \n", + "0 ClinGen:CA215070|OMIM:613653.0001 \n", + "0 ClinGen:CA215070|OMIM:613653.0001 \n", + "0 ClinGen:CA215070|OMIM:613653.0001 \n", + "0 ClinGen:CA215070|OMIM:613653.0001 \n", + "1 ClinGen:CA215072|OMIM:613653.0002 " + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# reduce data set\n", + "var_summary_merged_disease = var_summary_merged.copy()\n", + "var_summary_merged_disease = var_summary_merged_disease[[\n", + " 'VariationID', 'AlleleID', 'RS# (dbSNP)', 'Type', 'VariantName', 'RCVaccession',\n", + " 'LastEvaluated', 'ReviewStatus', 'ClinicalSignificance', 'ClinSigSimple',\n", + " 'NumberSubmitters', 'SubmitterCategories', 'Guidelines', 'TestedInGTR',\n", + " 'Origin', 'OriginSimple', 'Assembly', 'Phenotype', 'Citation', 'OtherIDs']]\n", + "var_summary_merged_disease.drop_duplicates(inplace=True)\n", + "\n", + "# expand results by disease identifier\n", + "cols = ['Phenotype']\n", + "for col in tqdm(cols): var_summary_merged_disease = var_summary_merged_disease.assign(**{col: var_summary_merged_disease[col].str.split(';')}).explode(col)\n", + " \n", + "# remove phenotype rows with None and drop duplicates\n", + "var_summary_merged_disease = var_summary_merged_disease[var_summary_merged_disease['Phenotype'] != 'None']\n", + "var_summary_merged_disease.drop_duplicates(inplace=True)\n", + "\n", + "# head prefix to output\n", + "var_summary_merged_disease['VariationID'] = 'clinvar_' + var_summary_merged_disease['VariationID'].astype(str)\n", + "\n", + "# print row count and preview data\n", + "print('There are {edge_count} edges'.format(edge_count=len(var_summary_merged_disease)))\n", + "var_summary_merged_disease.head(n=5)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [], + "source": [ + "# write data to file\n", + "var_summary_merged_disease.to_csv(open(processed_data_location + 'CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt', 'w'),\n", + " index=False, header=True, sep='\\t')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "<br>\n", + "\n", + "***\n", + "\n", + "### Uniprot Protein-Cofactor and Protein-Catalyst <a class=\"anchor\" id=\"uniprot-protein-cofactorcatalyst\"></a>\n", + "\n", + "**Data Source Wiki Page:** [Uniprot](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#uniprot-knowledgebase) \n", + "\n", + "**Purpose:** This script downloads the [uniprot-cofactor-catalyst.tab](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#uniprot-knowledgebase) file from the [Uniprot Knowledge Base](https://www.uniprot.org) in order to create the following edges: \n", + "- protein-cofactor \n", + "- protein-catalyst \n", + "\n", + "**Data:** This data was obtained by querying the [UniProt Knowledgebase](https://www.uniprot.org/uniprot/) using the *reviewed:yes AND organism:\"Homo sapiens (Human) [9606]\"\"* keyword and including the following columns:\n", + "- Entry (Standard) \n", + "- Status (Standard) \n", + "- PRO (*Miscellaneous*) \n", + "- ChEBI (Cofactor) (*Chemical entities*) \n", + "- ChEBI (Catalytic activity) (*Chemical entities*) \n", + "\n", + "The URL to access the results of this query is obtained by clicking on the share symbol and copying the free-text from the box. To obtain the data in a tab-delimited format the following string is appended to the end of the URL: \"&format=tab\".\n", + "\n", + "**NOTE.** Be sure to obtain a new URL from the [UniProt Knowledgebase](https://www.uniprot.org/uniprot/) when rebuilding to ensure you are getting the most up-to-date data. This query was last generated on `12/02/2020`.\n", + "\n", + "<br>\n", + "\n", + "**Output:** \n", + "- protein-cofactor ➞ `UNIPROT_PROTEIN_COFACTOR.txt`\n", + "- protein-catalyst ➞ `UNIPROT_PROTEIN_CATALYST.txt`\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'https://www.uniprot.org/uniprot/?query=&fil=organism%3A%22Homo%20sapiens%20(Human)%20%5B9606%5D%22&columns=id%2Creviewed%2Centry%20name%2Cdatabase(PRO)%2Cchebi(Cofactor)%2Cchebi(Catalytic%20activity)&format=tab'\n", + "if not os.path.exists(unprocessed_data_location + 'uniprot-cofactor-catalyst.tab'):\n", + " data_downloader(url, unprocessed_data_location, 'uniprot-cofactor-catalyst.tab')\n", + "\n", + "# upload data\n", + "data = open(unprocessed_data_location + 'uniprot-cofactor-catalyst.tab').readlines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# reformat data and write it out\n", + "with open(processed_data_location + 'UNIPROT_PROTEIN_COFACTOR.txt', 'w') as outfile1, open(processed_data_location + 'UNIPROT_PROTEIN_CATALYST.txt', 'w') as outfile2:\n", + " for line in tqdm(data):\n", + " status = line.split('\\t')[1]; upt_id = line.split('\\t')[0]; upt_entry = line.split('\\t')[2]\n", + " pr_id = 'PR_' + line.split('\\t')[3].strip(';')\n", + " # get cofactors\n", + " if 'CHEBI' in line.split('\\t')[4]: \n", + " for i in line.split('\\t')[4].split(';'):\n", + " chebi = i.split('[')[-1].replace(']', '').replace(':', '_')\n", + " outfile1.write(pr_id + '\\t' + chebi + '\\t' + status + '\\t' + upt_id + '\\t' + upt_entry + '\\n')\n", + " # get catalysts\n", + " if 'CHEBI' in line.split('\\t')[5]: \n", + " for i in line.strip('\\n').split('\\t')[5].split(';'):\n", + " chebi = i.split('[')[-1].replace(']', '').replace(':', '_')\n", + " outfile2.write(pr_id + '\\t' + chebi + '\\t' + status + '\\t' + upt_id + '\\t' + upt_entry + '\\n')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "**Cofactor Data** " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# load data, print row count, and preview it\n", + "pcp1_data = pandas.read_csv(processed_data_location + 'UNIPROT_PROTEIN_COFACTOR.txt', header=None,\n", + " names=['Protein_Ontology_IDs', 'CHEBI_IDs', 'Status', 'Uniprot_ID', 'Uniprot_Entry_name'],\n", + " delimiter='\\t')\n", + "\n", + "print('There are {edge_count} protein-cofactor edges'.format(edge_count=len(pcp1_data)))\n", + "pcp1_data.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "\n", + "**Catalyst Data** " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# load data, print row count, and preview it\n", + "pcp2_data = pandas.read_csv(processed_data_location + 'UNIPROT_PROTEIN_CATALYST.txt', header=None,\n", + " names=['Protein_Ontology_IDs', 'CHEBI_IDs', 'Status', 'Uniprot_ID', 'Uniprot_Entry_name'],\n", + " delimiter='\\t')\n", + "\n", + "print('There are {edge_count} protein-catalyst edges'.format(edge_count=len(pcp2_data)))\n", + "pcp2_data.head(n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "***\n", + "***\n", + "### NODE AND RELATION METADATA<a class=\"anchor\" id=\"node-relation-metadata\"></a>\n", + "***\n", + "\n", + "**Data Source Wiki Page:** [Dependencies](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies/#node-metadata) \n", + "\n", + "**Purpose:** The goal of this section is to obtain metadata for each entity that is not from an ontology and all relations used in the knowledge graph. \n", + "\n", + "<br>\n", + "\n", + "**Metadata:** \n", + "A variety of <u>metadata</u> are pulled from the data sources that are used to support external edges added to enhance the core set of ontologies. For the monthly PheknowLator builds, please see [`pheknowlator_source_metadata.xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/pheknowlator_source_metadata.xlsx) spreadsheet. This spreadsheet has two tabs, one for nodes and one for edges. Each each entity (i.e., node or relation) there are several columns, including descriptions of the metadata, the variable type, and even examples of values for each type of metadata. \n", + "\n", + "*Example Metadata Dictionary Output*. The code snippet below is meant to provide a snapshot of how data are organized in the metadata dictionary. As demonstrated by this example, there are three high-level keys: \n", + " - `nodes`: Nodes are keyed by CURIE. Every node has a `Label`, `Description`, `Synonym`, and `Dbxref` (whenever possible). Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. \n", + " - `edges`: Edges are keyed by a label which represents the edge type (the same label that is used in `resource_info.txt` and `edge_source_list.txt` files). Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. \n", + " - `relations`: Relations or `owl:ObjectProperty` objects are keyed by CURIE. Similar to nodes, every relation has a `Label`, `Description`, and `Synonym` (whenever possible). Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. \n", + "\n", + "```python\n", + "{\n", + " 'nodes': {\n", + " 'NCBIGene_2052': {\n", + " 'Label': 'EPHX1',\n", + " 'Description': \"EPHX1 has locus group 'protein-coding' and is located on chromosome 1 (1q42.12).\",\n", + " 'Synonym': 'epoxide hydrolase 1, microsomal (xenobiotic)|epoxide hydratase|EPHX|HYL1|MEHepoxide hydrolase 1|epoxide hydrolase 1 microsomal|EPOX',\n", + " 'Dbxref': 'MIM:132810|HGNC:HGNC:3401|Ensembl:ENSG00000143819', ... },\n", + " 'CHEBI_4592': {\n", + " 'Label': 'Dihydroxycarbazepine',\n", + " 'Description': \"None\",\n", + " 'Synonym': '10,11-Dihydro-10,11-dihydroxy-5H-dibenzazepine-5-carboxamide|10,11-Dihydroxycarbamazepine',\n", + " 'Dbxref': 'CAS:35079-97-1|KEGG:C07495',\n", + " 'CTD_chem_gene_ixns.tsv.gz': { \n", + " 'CTD_ChemicalID': {'MESH:C004822'},\n", + " 'CTD_CasRN': {'35079-97-1'},\n", + " 'CTD_ChemicalName': {'10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine-5-carboxamide'}}, ... }, ... },\n", + " 'edges': {\n", + " 'chemical-gene': {\n", + " 'CHEBI_4592-NCBIGene_2052': {\n", + " {'CTD_chem_gene_ixns.tsv': {\n", + " 'CTD_Evidence': [{'CTD_Interaction': '[EPHX1 gene SNP affects the metabolism of carbamazepine epoxide] which affects the chemical synthesis of 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine-5-carboxamide',\n", + " 'CTD_InteractionActions': 'affects^chemical synthesis|affects^metabolic processing',\n", + " 'CTD_PubMedIDs': '15692831'}]}}, ...}, ...}, ...}, \n", + " 'relations': {\n", + " 'RO_0002434': {\n", + " 'Label': 'interacts with',\n", + " 'Description': 'A relationship that holds between two entities in which the processes executed by the two entities are causally connected.',\n", + " 'Synonym': 'in pairwise interaction with'}, ... }\n", + "}\n", + "```\n", + "\n", + "<br>\n", + "\n", + "\n", + "<i><b>NOTE.</b> All entity metadata are written to the `metadata` directory as a `pickled` dictionary called `entity_metadata_dict.pkl`. The algorithm will look for this dictionary in the `metadata` directory and if it is not there, then no entity metadata will be created.</i>\n", + "\n", + "<br>\n", + "\n", + "### Prepare Metadata Dictionaries\n", + "***\n", + "\n", + "**Purpose:** To create the resources needed in order to create metadata dictionaries. This process has the following steps:\n", + "\n", + "**1. [Generate Metadata Dictionaries](#generate-metadata-dictionaries):** In order to obtain metadata, we first read in the data source for each type and convert it into a dictionary. Then, each metadata dictionary is merged together and saved to a `master_metadata_dictionary`, keyed by identifier.\n", + " - <u>Input Datasets</u>: \n", + " - [CTD_chem_gene_ixns.tsv](http://ctdbase.org/reports/CTD_chem_gene_ixns.tsv.gz) \n", + " - Edges: `chemical-gene`, `chemical-protein`, `chemical-rna` \n", + " - Identifier Maps: \n", + " - Chemicals: [MESH_CHEBI_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/MESH_CHEBI_MAP.txt) \n", + " - Proteins: [ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt) \n", + " - RNA: [ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt) \n", + " - [CTD_chem_go_enriched.tsv](http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz) \n", + " - Edges: `chemical-gobp`, `chemical-gocc`, `chemical-gomf` \n", + " - Identifier Maps: \n", + " - Chemicals: [MESH_CHEBI_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/MESH_CHEBI_MAP.txt) \n", + " - [CTD_chemicals_diseases.tsv](http://ctdbase.org/reports/CTD_chemicals_diseases.tsv.gz) \n", + " - Edges: `chemical-disease`, `chemical-phenotype` \n", + " - Identifier Maps: \n", + " - Chemicals: [MESH_CHEBI_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/MESH_CHEBI_MAP.txt) \n", + " - Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt) \n", + " - Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) \n", + " - [ChEBI2Reactome_All_Levels.txt](https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt) \n", + " - Edge: `chemical-pathway` \n", + " - [goa_human.gaf](http://current.geneontology.org/annotations/goa_human.gaf.gz) \n", + " - Edges: `protein-gobp`, `protein-gocc`, `protein-gomf` \n", + " - Identifier Maps: \n", + " - Proteins: [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", + " - [COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt](http://genemania.org/data/current/Homo_sapiens.COMBINED/COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt) \n", + " - Edge: `gene-gene` \n", + " - Identifier Maps: \n", + " - Genes: [UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt) \n", + " - [phenotype.hpoa](http://purl.obolibrary.org/obo/hp/hpoa/phenotype.hpoa) \n", + " - Edge: `disease-phenotype` \n", + " - Identifier Maps: \n", + " - Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt) \n", + " - [ChEBI2Reactome_All_Levels.txt](https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt) \n", + " - Edge: `chemical-pathway` \n", + " - [gene_association.reactome](https://reactome.org/download/current/gene_association.reactome.gz) \n", + " - Edge: `gobp-pathway`, `pathway-gocc`, `pathway-gomf` \n", + " - [UniProt2Reactome_All_Levels.txt](https://reactome.org/download/current/UniProt2Reactome_All_Levels.txt) \n", + " - Edge: `protein-pathway` \n", + " - Identifier Maps: \n", + " - Proteins: [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", + " - [CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt) \n", + " - Edge: `variant-disease`, `variant-disease` \n", + " - Identifier Maps: \n", + " - Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt)\n", + " - Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) \n", + " - [CLINVAR_VARIANT_GENE_EDGES.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_EDGES.txt) \n", + " - Edge: `variant-gene` \n", + " - [HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt) \n", + " - Edge: `protein-anatomy`, `protein-cell`, `rna-anatomy`, `rna-cell` \n", + " - Identifier Maps: \n", + " - Proteins: [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", + "\t\t- Anatomy: [HPA_GTEx_TISSUE_CELL_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt)\n", + " - Cells: [HPA_GTEx_TISSUE_CELL_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt) \n", + " - RNA: [GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt) \n", + " - [UNIPROT_PROTEIN_CATALYST.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_CATALYST.txt) \n", + " - Edge: `protein-catalyst`\n", + " - [UNIPROT_PROTEIN_COFACTOR.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_COFACTOR.txt) \n", + " - Edge: `protein-cofactor`\n", + " - [9606.protein.links.v11.0.txt.gz](https://stringdb-static.org/download/protein.links.v11.0/9606.protein.links.v11.0.txt.gz) \n", + " - Edge: `protein-protein` \n", + " - Identifier Maps: \n", + " - Proteins: [STRING_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/STRING_PRO_ONTOLOGY_MAP.txt)\n", + " - [curated_gene_disease_associations.tsv](https://www.disgenet.org/static/disgenet_ap1/files/downloads/curated_gene_disease_associations.tsv.gz) \n", + " - Edge: `gene-disease`, `gene-phenotype` \n", + " - Identifier Maps: \n", + " - Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt)\n", + " - Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) \n", + " \n", + "<br>\n", + "\n", + "**2. [Write Metadata Files](#write-metadata-files):** The `master_metadata_dictionary` dictionary from _Step 1_ is `pickled` and saved to the `resources/metadata/entity_metadata_dict.pkl` directory.\n", + "\n", + "<br>\n", + "\n", + "***" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# create the shell for the node and relation dictionary\n", + "master_metadata_dictionary = {'nodes': {}, 'relations': {}, 'edges': {}}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "### Generate Metadata Dictionaries <a class=\"anchor\" id=\"generate-metadata-dictionaries\"></a>\n", + "\n", + "There are two types of data that are processed when building the metadata dictionary. The first type of data is *Primary*, meaning it consists of a small set of variables that are collected for all entities that are included in the knowledge graph (i.e., `Label`, `Description`, `DbXref`, `Synonym`). These data are collected for entities of type: genes, RNA, variants, and pathways. *Secondary* data are then collected for all edges in the knowledge graph that include entities that are not obtained from an ontology. For these sources, metadata may differ by source. \n", + "- [Primary Metadata Elements](#primary) \n", + "- [Secondary Metadata Elements](#secondary)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Primary Metadata Elements<a class=\"anchor\" id=\"primary\"></a> \n", + "\n", + "***" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Genes Metadata Dictionary <a class=\"anchor\" id=\"gene-metadata\"></a>\n", + "\n", + "The nested dictionary of gene metadata is created by looping over the merged data described in the prior column. The `keys` of the dictionary are `Entrez gene identifiers` and the `values` are dictionaries for each metadata type." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# entrez gene data\n", + "entrez_gene_data = pandas.read_csv(unprocessed_data_location + 'Homo_sapiens.gene_info', header=0, delimiter='\\t', low_memory=False)\n", + "\n", + "# remove all rows that are not human\n", + "entrez_gene_data = entrez_gene_data.loc[entrez_gene_data['#tax_id'].apply(lambda x: x == 9606)]\n", + "\n", + "# replace NaN and '-' with 'None'\n", + "entrez_gene_data.fillna('None', inplace=True)\n", + "entrez_gene_data.replace('-','None', inplace=True, regex=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 65015/65015 [00:20<00:00, 3173.30it/s]\n" + ] + } + ], + "source": [ + "# create metadata\n", + "for idx, row in tqdm(entrez_gene_data.iterrows(), total=entrez_gene_data.shape[0]):\n", + " genes, lab, desc, syn = [], [], [], []\n", + " gene_id, sym, defn, gene_type = 'NCBIGene_' + str(row['GeneID']), row['Symbol'], row['description'], row['type_of_gene']\n", + " chrom, map_loc, s1, s2 = row['chromosome'], row['map_location'], row['Synonyms'], row['Other_designations']\n", + " dbxref = row['dbXrefs']\n", + " if gene_id != 'None':\n", + " genes.append('http://www.ncbi.nlm.nih.gov/gene/' + str(gene_id))\n", + " if sym != 'None' or sym != '': lab.append(sym)\n", + " else: lab.append('Entrez_ID:' + gene_id)\n", + " if 'None' not in [defn, gene_type, chrom, map_loc]:\n", + " desc_str = \"{} has locus group '{}' and is located on chromosome {} ({}).\"\n", + " desc.append(desc_str.format(sym, gene_type, chrom, map_loc))\n", + " else: desc.append(\"{} locus group '{}'.\".format(sym, gene_type))\n", + " if s1 != 'None' and s2 != 'None': syn.append('|'.join(set([x for x in (s1 + s2).split('|') if x != 'None' or x != ''])))\n", + " elif s1 != 'None': syn.append('|'.join(set([x for x in s1.split('|') if x != 'None' or x != ''])))\n", + " elif s2 != 'None': syn.append('|'.join(set([x for x in s2.split('|') if x != 'None' or x != ''])))\n", + " else: syn.append('None')\n", + " \n", + " # update master dictionary\n", + " master_metadata_dictionary['nodes'][gene_id] = {\n", + " 'Label': ''.join(lab),\n", + " 'Description': ''.join(desc),\n", + " 'Synonym': '|'.join(syn),\n", + " 'Dbxref': dbxref}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### RNA Metadata Dictionary <a class=\"anchor\" id=\"rna-metadata\"></a>\n", + "\n", + "The nested dictionary of rna metadata is created by looping over the cleaned human [Ensembl](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#ensembl) gene, RNA, and protein identifier data set (`ensembl_identifier_data_cleaned.txt`). The `keys` of the dictionary are `Ensembl transcript identifiers` and the `values` are dictionaries for each metadata type." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# load data\n", + "rna_gene_data = pandas.read_csv(processed_data_location + 'ensembl_identifier_data_cleaned.txt', header=0, delimiter='\\t', low_memory=False)\n", + "\n", + "# remove rows without identifiers\n", + "rna_gene_data = rna_gene_data.loc[rna_gene_data['transcript_stable_id'].apply(lambda x: x != 'None')]\n", + "\n", + "# remove unneeded columns\n", + "rna_gene_data.drop(['ensembl_gene_id', 'symbol', 'protein_stable_id', 'uniprot_id', 'master_transcript_type',\n", + " 'entrez_id', 'ensembl_gene_type', 'master_gene_type', 'symbol'], axis=1, inplace=True)\n", + "\n", + "# remove duplicates\n", + "rna_gene_data.drop_duplicates(subset=['transcript_stable_id', 'transcript_name', 'ensembl_transcript_type'], keep='first', inplace=True)\n", + "\n", + "# replace NaN with 'None'\n", + "rna_gene_data.fillna('None', inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 248494/248494 [00:45<00:00, 5411.57it/s]\n" + ] + } + ], + "source": [ + "# create metadata\n", + "for idx, row in tqdm(rna_gene_data.iterrows(), total=rna_gene_data.shape[0]):\n", + " rna, lab, desc, syn = [], [], [], []\n", + " rna_id = 'ensembl_' + row['transcript_stable_id']\n", + " ent_type, nme = row['ensembl_transcript_type'], row['transcript_name']\n", + " rna.append('https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=' + rna_id)\n", + " if nme != 'None': lab.append(nme)\n", + " else:\n", + " lab.append('Ensembl_Transcript_ID:' + rna_id)\n", + " nme = 'Ensembl_Transcript_ID:' + rna_id\n", + " if ent_type != 'None': desc.append(\"Transcript {} is classified as type '{}'.\".format(nme, ent_type))\n", + " else: desc.append('None')\n", + " syn.append('None')\n", + " \n", + " # update master dictionary\n", + " master_metadata_dictionary['nodes'][rna_id] = {\n", + " 'Label': ''.join(lab),\n", + " 'Description': ''.join(desc),\n", + " 'Synonym': '|'.join(syn)}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Variant Metadata Dictionary <a class=\"anchor\" id=\"variant-metadata\"></a> \n", + "\n", + "The nested dictionary of rna metadata is created by looping over the human [ClinVar Variant](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#clinvar) identifier data set (`variant_summary.txt`). The `keys` of the dictionary are `dbSNP identifiers` and the `values` are dictionaries for each metadata type." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'ftp://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'variant_summary.txt'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "\n", + "# load data\n", + "var_data = pandas.read_csv(unprocessed_data_location + 'variant_summary.txt', header=0, delimiter='\\t', low_memory=False)\n", + "\n", + "# remove rows without identifiers\n", + "var_data = var_data.loc[var_data['Assembly'].apply(lambda x: x == 'GRCh38')]\n", + "var_data = var_data.loc[var_data['RS# (dbSNP)'].apply(lambda x: x != -1)]\n", + "\n", + "# de-dup data\n", + "var_metadata = var_data[['VariationID', '#AlleleID', 'Type', 'Name', 'ClinicalSignificance', 'RS# (dbSNP)', 'Origin',\n", + " 'ChromosomeAccession', 'Chromosome', 'Start', 'Stop', 'ReferenceAllele', 'OtherIDs',\n", + " 'Assembly', 'AlternateAllele','Cytogenetic', 'ReviewStatus', 'LastEvaluated']] \n", + "\n", + "# replace NaN with 'None'\n", + "var_metadata.replace('na', 'None', inplace=True)\n", + "var_metadata.fillna('None', inplace=True)\n", + "\n", + "# remove duplicate dbSNP ids by choosing the most recent reviewed variant\n", + "var_metadata.sort_values('LastEvaluated', ascending=False, inplace=True)\n", + "var_metadata.drop_duplicates(subset='RS# (dbSNP)', keep='first', inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 598286/598286 [03:42<00:00, 2691.06it/s]\n" + ] + } + ], + "source": [ + "# create metadata\n", + "for idx, row in tqdm(var_metadata.iterrows(), total=var_metadata.shape[0]):\n", + " variant, label, desc, syn = [], [], [], []\n", + " var_id, lab, dbxref = 'clinvar_' + str(row['VariationID']), row['Name'], row['OtherIDs']\n", + " if var_id != 'None':\n", + " variant.append('https://www.ncbi.nlm.nih.gov/snp/rs' + str(var_id))\n", + " if lab != 'None': label.append(lab)\n", + " else: label.append('dbSNP_ID:rs' + str(var_id))\n", + " sent = \"This variant is a {} {} located on chromosome {} ({}, start:{}/stop:{} positions, \" +\\\n", + " \"cytogenetic location:{}) and has clinical significance '{}'. \" +\\\n", + " \"This entry is for the {} and was last reviewed on {} with review status '{}'.\"\n", + " desc.append(sent.format(row['Origin'].replace(';', '/'), row['Type'].replace(';', '/'), row['Chromosome'], row['ChromosomeAccession'],\n", + " row['Start'], row['Stop'], row['Cytogenetic'], row['ClinicalSignificance'],\n", + " row['Assembly'], row['LastEvaluated'], row['ReviewStatus']).replace('None', 'UNKNOWN'))\n", + " syn.append('None')\n", + " \n", + " # update master dictionary\n", + " master_metadata_dictionary['nodes'][var_id] = {\n", + " 'Label': ''.join(lab),\n", + " 'Description': ''.join(desc),\n", + " 'Synonym': '|'.join(syn),\n", + " 'Dbxref': dbxref}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Pathway Metadata Dictionary <a class=\"anchor\" id=\"pathway-metadata\"></a> \n", + "\n", + "The nested dictionary of pathway metadata is created by looping over the human [Reactome Pathway Database](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#reactome-pathway-database) identifier data set (`ReactomePathways.txt`); Reactome-Gene Association data (`gene_association.reactome.gz`), and Reactome-ChEBI data (`ChEBI2Reactome_All_Levels.txt`). The `keys` of the dictionary are `Reactome identifiers` and the `values` are dictionaries for each metadata type." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# download reactome pathways data\n", + "url = 'https://reactome.org/download/current/ReactomePathways.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'ReactomePathways.txt'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "# load data\n", + "reactome_pathways = pandas.read_csv(unprocessed_data_location + 'ReactomePathways.txt', header=None, delimiter='\\t', low_memory=False)\n", + "reactome_pathways = reactome_pathways.loc[reactome_pathways[2].apply(lambda x: x == 'Homo sapiens')] \n", + "\n", + "# download reactome gene association data\n", + "url = 'https://reactome.org/download/current/gene_association.reactome.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'gene_association.reactome'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "# load data\n", + "reactome_pathways2 = pandas.read_csv(unprocessed_data_location + 'gene_association.reactome', header=None, delimiter='\\t', skiprows=4, low_memory=False)\n", + "reactome_pathways2 = reactome_pathways2.loc[reactome_pathways2[12].apply(lambda x: x == 'taxon:9606')]\n", + "reactome_pathways2[5] = reactome_pathways2[5].str.replace('REACTOME:','', regex=True) \n", + "\n", + "# download reactome CHEBI data\n", + "url = 'https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'ChEBI2Reactome_All_Levels.txt'):\n", + " data_downloader(url, unprocessed_data_location)\n", + "# load data\n", + "reactome_pathways3 = pandas.read_csv(unprocessed_data_location + 'ChEBI2Reactome_All_Levels.txt', header=None, delimiter='\\t', low_memory=False)\n", + "# remove all non-human pathways and save as list\n", + "reactome_pathways3 = reactome_pathways3.loc[reactome_pathways3[5].apply(lambda x: x == 'Homo sapiens')] " + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 711/711 [03:48<00:00, 3.11it/s]\n" + ] + } + ], + "source": [ + "# get metadata\n", + "nodes = list(set(reactome_pathways[0]) | set(reactome_pathways2[5]) | set(reactome_pathways3[1]))\n", + "pathway_metadata_final = metadata_api_mapper(nodes)\n", + "\n", + "# update dictionary\n", + "pathway_metadata_final['ID'] = pathway_metadata_final['ID'].map('reactome_{}'.format)\n", + "pathway_metadata_final.set_index('ID', inplace=True)\n", + "\n", + "# add entries to existing dictionary\n", + "master_metadata_dictionary['nodes'].update(pathway_metadata_final.to_dict('index'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Relations Metadata Dictionary <a class=\"anchor\" id=\"relations-metadata\"></a> \n", + "\n", + "The nested dictionary of relation metadata is created by looping over the human [Relations Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#relations-ontology) identifier data set (`ro_with_imports.owl`). The `keys` of the dictionary are `Relations Ontology identifiers` and the `values` are dictionaries for each metadata type." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 8089 edges in the ontology (date:01/22/2022)\n" + ] + } + ], + "source": [ + "# download ontology\n", + "if not os.path.exists(unprocessed_data_location + 'ro_with_imports.owl'):\n", + " command = '{} {} --merge-import-closure -o {}'\n", + " os.system(command.format(owltools_location, 'http://purl.obolibrary.org/obo/ro.owl',\n", + " unprocessed_data_location + 'ro_with_imports.owl'))\n", + "# load graph\n", + "ro_graph = Graph().parse(unprocessed_data_location + 'ro_with_imports.owl')\n", + "print('There are {} edges in the ontology (date:{})'.format(len(ro_graph), datetime.datetime.now().strftime('%m/%d/%Y')))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 610/610 [00:00<00:00, 5187.92it/s]\n" + ] + } + ], + "source": [ + "# get metadata\n", + "relation_metadata_dict, obo = {}, Namespace('http://purl.obolibrary.org/obo/')\n", + "\n", + "# get ontology information\n", + "cls = [x for x in gets_ontology_classes(ro_graph) if '/RO_' in str(x)] +\\\n", + " [x for x in gets_object_properties(ro_graph) if '/RO_' in str(x)]\n", + "master_synonyms = [x for x in ro_graph if 'synonym' in str(x[1]).lower() and isinstance(x[0], URIRef)]\n", + "\n", + "for x in tqdm(cls):\n", + " # labels\n", + " cls_label = [x for x in ro_graph.objects(x, RDFS.label) if '@' not in n3(x) or '@en' in n3(x)]\n", + " labels = str(cls_label[0]) if len(cls_label) > 0 else 'None'\n", + " # synonyms\n", + " cls_syn = [str(i[2]) for i in master_synonyms if x == i[0]]\n", + " synonym = str(cls_syn[0]) if len(cls_syn) > 0 else 'None'\n", + " # description\n", + " cls_desc = [x for x in ro_graph.objects(x, obo.IAO_0000115) if '@' not in n3(x) or '@en' in n3(x)]\n", + " desc = '|'.join([str(cls_desc[0])]) if len(cls_desc) > 0 else 'None'\n", + " \n", + " relation_metadata_dict[str(x).split('/')[-1]] = {\n", + " 'Label': labels, 'Description': desc, 'Synonym': synonym\n", + " }\n", + "\n", + "# add entries to existing dictionary\n", + "master_metadata_dictionary['relations'].update(relation_metadata_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "#### Secondary Metadata Elements<a class=\"anchor\" id=\"secondary\"></a> \n", + "\n", + "***" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Download Identifier Maps\n", + "\n", + "This code chunk downloads identifier mapping files that were creating in the prior steps.\n", + "\n", + "- Chemicals: [MESH_CHEBI_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/MESH_CHEBI_MAP.txt) \n", + "- Genes: [UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt) \n", + "- Proteins: \n", + " - [ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt) \n", + " - [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", + " - [STRING_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/STRING_PRO_ONTOLOGY_MAP.txt)\n", + "- RNA: [ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt)\n", + "- Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt) \n", + "- Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) " + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": {}, + "outputs": [], + "source": [ + "# entrez-ensembl map\n", + "rna_map = pandas.read_csv(processed_data_location + 'ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt',\n", + " header=None, delimiter='\\t', low_memory=False,\n", + " names=['Entrez_Gene_IDs', 'Ensembl_Transcript_IDs', 'Entrez_Gene_Type',\n", + " 'Ensembl_Transcript_Type', 'Master_Gene_Type', 'Master_Transcript_Type',\n", + " 'Entrez_Gene_prefix'])\n", + "# entrez-pro map\n", + "entrez_pro_map = pandas.read_csv(processed_data_location + 'ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt',\n", + " header=None, delimiter='\\t', low_memory=False, usecols = [0, 1, 2, 4],\n", + " names=['Gene_IDs', 'Protein_Ontology_IDs', 'Entrez_Gene_Type',\n", + " 'Master_Gene_Type', 'Entrez_Gene_Prefix'])\n", + "# symbol-ensembl map\n", + "symbol_transcript_map = pandas.read_csv(processed_data_location + 'GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt',\n", + " header=None, delimiter='\\t', low_memory=False,\n", + " names=['Gene_Symbols', 'Ensembl_Transcript_IDs',\n", + " 'Gene_Type', 'Ensembl_Transcript_Type',\n", + " 'Master_Gene_Type', 'Master_Transcript_Type'])\n", + "\n", + "# string-pro map\n", + "string_pro_map = pandas.read_csv(processed_data_location + 'STRING_PRO_ONTOLOGY_MAP.txt',\n", + " header=None, delimiter='\\t', low_memory=False, usecols=[0, 1],\n", + " names=['STRING_IDs', 'Protein_Ontology_IDs'])\n", + "# uniprot-pro map\n", + "uniprot_pro_map = pandas.read_csv(processed_data_location + 'UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt',\n", + " header=None, delimiter='\\t', low_memory=False, usecols=[0, 1],\n", + " names=['Uniprot_Accession_IDs', 'Protein_Ontology_IDs'])\n", + "# uniprot-entrez gene map\n", + "uniprot_entrez_data = pandas.read_csv(processed_data_location + 'UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt',\n", + " header=None, delimiter='\\t', low_memory=False, usecols=[0, 1, 2, 3],\n", + " names=['Uniprot_Accession_IDs', 'Entrez_Gene_IDs',\n", + " 'master_gene_type', 'gene_type_update'])\n", + "# mesh-chebi map\n", + "mesh_chebi_map = pandas.read_csv(processed_data_location + 'MESH_CHEBI_MAP.txt', header=None, \n", + " names=['MESH_ID', 'CHEBI_ID'], delimiter='\\t')\n", + "# disease maps\n", + "disease_maps = pandas.read_csv(processed_data_location + 'DISEASE_MONDO_MAP.txt', header=None,\n", + " names=['Disease_IDs', 'MONDO_IDs'], delimiter='\\t')\n", + "# phenotype maps\n", + "phenotype_maps = pandas.read_csv(processed_data_location + 'PHENOTYPE_HPO_MAP.txt', header=None,\n", + " names=['Disease_IDs', 'HP_IDs'], delimiter='\\t')\n", + "\n", + "# cells and anatomical entities\n", + "anatomy_maps = pandas.read_csv(processed_data_location + 'HPA_GTEx_TISSUE_CELL_MAP.txt', header=None,\n", + " names=['anatomy_ids', 'ontolgoy_ids'], delimiter='\\t')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "##### Genomic Entity Metadata\n", + "\n", + "Process the dictionary created in the prior steps in order to assist with creating a master metadata file for all nodes that are a genomic entity (i.e., genes, transcripts, or proteins)." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 876333/876333 [00:30<00:00, 28512.58it/s] \n" + ] + } + ], + "source": [ + "# clean up data for use with master metadata\n", + "genomic_metadata = dict()\n", + "for key, value in tqdm(reformatted_mapped_identifiers.items()):\n", + " old_prefix = '_'.join(key.split('_')[0:-1]); idx = key.split('_')[-1]; pass_var = True; new_prefix = None\n", + " if old_prefix == 'entrez_id': new_prefix = 'NCBIGene'\n", + " elif old_prefix in ['ensembl_gene_id', 'protein_stable_id', 'transcript_stable_id']: new_prefix = 'ensembl'\n", + " elif old_prefix == 'pro_id_PR': new_prefix = 'PR'\n", + " else: pass_var = False\n", + " if pass_var and new_prefix is not None:\n", + " updated_key = new_prefix + '_' + idx; master_metadata_dict = {updated_key: {}}\n", + " for x in value:\n", + " i, j = '_'.join(x.split('_')[0:-1]), x.split('_')[-1]\n", + " if 'type' in i: continue\n", + " elif i == 'entrez_id': new_i = 'NCBIGene'; j = new_i + '_' + j\n", + " elif i == 'ensembl_gene_id': new_i = 'ensembl gene'; j = 'ensembl_' + j\n", + " elif i == 'protein_stable_id': new_i = 'ensembl protein'; j = 'ensembl_' + j\n", + " elif i == 'transcript_stable_id': new_i = 'ensembl transcript'; j = 'ensembl_' + j\n", + " elif i == 'pro_id_PR': new_i = 'PR'; j = new_i + '_' + j\n", + " elif i == 'hgnc_id': new_i = 'HGNC_ID'; j = new_i + '_' + j\n", + " elif i == 'uniprot_id': new_i = 'uniprot'; j = new_i + '_' + j\n", + " elif i == 'symbol': new_i = 'GeneSymbol'; j = new_i + '_' + j\n", + " else:\n", + " if i == 'synonyms': new_i = 'Synonyms'\n", + " elif i == 'name': new_i = 'Label'\n", + " elif i == 'Other_designations': new_i = 'Synonyms'; j = j.split('|')\n", + " else: new_i = i\n", + " if new_i in master_metadata_dict[updated_key].keys():\n", + " if isinstance(j , list): master_metadata_dict[updated_key][new_i] += j\n", + " else: master_metadata_dict[updated_key][new_i] += [j]\n", + " else: master_metadata_dict[updated_key][new_i] = [j]\n", + " genomic_metadata[updated_key] = master_metadata_dict" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `CTD_chem_gene_ixns.tsv` <a class=\"anchor\" id=\"chemical-gene\"></a>\n", + "\n", + "**Edges:** \n", + "- `chemical-gene` \n", + "- `chemical-protein` \n", + "- `chemical-rna` \n", + "\n", + "**Identifier Maps:** \n", + "- Chemicals: [MESH_CHEBI_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/MESH_CHEBI_MAP.txt) \n", + "- Proteins: [ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt) \n", + "- RNA: [ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt)\n", + "\n", + "This chunk process the [`CTD_chem_gene_ixns.tsv`](http://ctdbase.org/reports/CTD_chem_gene_ixns.tsv.gz) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `ChemicalID`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, MeSH Identifier. Variable is provided as a string without a prefix. \n", + " - `CasRN`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, a string containing a CAS Registry Number, if available. \n", + " - `ChemicalName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. If not, a string containing the name of the chemical. \n", + "- **Edges:** \n", + " - `Interaction`: A string describing a chemical-gene/protein/rna interaction. \n", + " - `InteractionActions`: A \"|\"-delimited list of the actions that underlie an interaction. \n", + " - `PubMedIDs`: |'-delimited list of PubMed identifiers that do not include a prefix. " + ] + }, + { + "cell_type": "code", + "execution_count": 438, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'http://ctdbase.org/reports/CTD_chem_gene_ixns.tsv.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'CTD_chem_gene_ixns.tsv'):\n", + " data_downloader(url, unprocessed_data_location, 'CTD_chem_gene_ixns.tsv')\n", + "\n", + "# load data\n", + "ctd_gene_inx = pandas.read_csv(unprocessed_data_location + 'CTD_chem_gene_ixns.tsv', header=0, delimiter='\\t', skiprows=27)\n", + "ctd_gene_inx = ctd_gene_inx[ctd_gene_inx['# ChemicalName'] != '#']\n", + "ctd_gene_inx = ctd_gene_inx[ctd_gene_inx['OrganismID'] == 9606]\n", + "ctd_gene_inx = ctd_gene_inx[ctd_gene_inx['PubMedIDs'] != numpy.nan]\n", + "ctd_gene_inx.fillna('None', inplace=True)\n", + "# fix variable typing\n", + "ctd_gene_inx['GeneID'] = ctd_gene_inx['GeneID'].astype('Int64')\n", + "ctd_gene_inx['OrganismID'] = ctd_gene_inx['OrganismID'].astype('Int64')\n", + "# update prefix\n", + "ctd_gene_inx['ChemicalID'] = 'MESH:' + ctd_gene_inx['ChemicalID']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Merge Identifier Maps*" + ] + }, + { + "cell_type": "code", + "execution_count": 439, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th># ChemicalName</th>\n", + " <th>ChemicalID</th>\n", + " <th>CasRN</th>\n", + " <th>GeneSymbol</th>\n", + " <th>GeneID</th>\n", + " <th>GeneForms</th>\n", + " <th>Organism</th>\n", + " <th>OrganismID</th>\n", + " <th>Interaction</th>\n", + " <th>InteractionActions</th>\n", + " <th>...</th>\n", + " <th>Ensembl_Transcript_IDs</th>\n", + " <th>Entrez_Gene_Type_x</th>\n", + " <th>Ensembl_Transcript_Type</th>\n", + " <th>Master_Gene_Type</th>\n", + " <th>Master_Transcript_Type</th>\n", + " <th>Entrez_Gene_prefix</th>\n", + " <th>Gene_IDs</th>\n", + " <th>Protein_Ontology_IDs</th>\n", + " <th>Entrez_Gene_Type_y</th>\n", + " <th>Entrez_Gene_Prefix</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", + " <td>MESH:C004822</td>\n", + " <td>35079-97-1</td>\n", + " <td>EPHX1</td>\n", + " <td>2052</td>\n", + " <td>gene</td>\n", + " <td>Homo sapiens</td>\n", + " <td>9606</td>\n", + " <td>[EPHX1 gene SNP affects the metabolism of carb...</td>\n", + " <td>affects^chemical synthesis|affects^metabolic p...</td>\n", + " <td>...</td>\n", + " <td>ensembl_ENST00000467015</td>\n", + " <td>protein-coding</td>\n", + " <td>processed_transcript</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " <td>NCBIGene_2052</td>\n", + " <td>2052</td>\n", + " <td>PR_P07099</td>\n", + " <td>protein-coding</td>\n", + " <td>NCBIGene_2052</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", + " <td>MESH:C004822</td>\n", + " <td>35079-97-1</td>\n", + " <td>EPHX1</td>\n", + " <td>2052</td>\n", + " <td>gene</td>\n", + " <td>Homo sapiens</td>\n", + " <td>9606</td>\n", + " <td>[EPHX1 gene SNP affects the metabolism of carb...</td>\n", + " <td>affects^chemical synthesis|affects^metabolic p...</td>\n", + " <td>...</td>\n", + " <td>ensembl_ENST00000366837</td>\n", + " <td>protein-coding</td>\n", + " <td>protein_coding</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " <td>NCBIGene_2052</td>\n", + " <td>2052</td>\n", + " <td>PR_P07099</td>\n", + " <td>protein-coding</td>\n", + " <td>NCBIGene_2052</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", + " <td>MESH:C004822</td>\n", + " <td>35079-97-1</td>\n", + " <td>EPHX1</td>\n", + " <td>2052</td>\n", + " <td>gene</td>\n", + " <td>Homo sapiens</td>\n", + " <td>9606</td>\n", + " <td>[EPHX1 gene SNP affects the metabolism of carb...</td>\n", + " <td>affects^chemical synthesis|affects^metabolic p...</td>\n", + " <td>...</td>\n", + " <td>ensembl_ENST00000272167</td>\n", + " <td>protein-coding</td>\n", + " <td>protein_coding</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " <td>NCBIGene_2052</td>\n", + " <td>2052</td>\n", + " <td>PR_P07099</td>\n", + " <td>protein-coding</td>\n", + " <td>NCBIGene_2052</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>3 rows × 24 columns</p>\n", + "</div>" + ], + "text/plain": [ + " # ChemicalName ChemicalID \\\n", + "0 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", + "1 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", + "2 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", + "\n", + " CasRN GeneSymbol GeneID GeneForms Organism OrganismID \\\n", + "0 35079-97-1 EPHX1 2052 gene Homo sapiens 9606 \n", + "1 35079-97-1 EPHX1 2052 gene Homo sapiens 9606 \n", + "2 35079-97-1 EPHX1 2052 gene Homo sapiens 9606 \n", + "\n", + " Interaction \\\n", + "0 [EPHX1 gene SNP affects the metabolism of carb... \n", + "1 [EPHX1 gene SNP affects the metabolism of carb... \n", + "2 [EPHX1 gene SNP affects the metabolism of carb... \n", + "\n", + " InteractionActions ... \\\n", + "0 affects^chemical synthesis|affects^metabolic p... ... \n", + "1 affects^chemical synthesis|affects^metabolic p... ... \n", + "2 affects^chemical synthesis|affects^metabolic p... ... \n", + "\n", + " Ensembl_Transcript_IDs Entrez_Gene_Type_x Ensembl_Transcript_Type \\\n", + "0 ensembl_ENST00000467015 protein-coding processed_transcript \n", + "1 ensembl_ENST00000366837 protein-coding protein_coding \n", + "2 ensembl_ENST00000272167 protein-coding protein_coding \n", + "\n", + " Master_Gene_Type Master_Transcript_Type Entrez_Gene_prefix Gene_IDs \\\n", + "0 protein-coding protein-coding NCBIGene_2052 2052 \n", + "1 protein-coding protein-coding NCBIGene_2052 2052 \n", + "2 protein-coding protein-coding NCBIGene_2052 2052 \n", + "\n", + " Protein_Ontology_IDs Entrez_Gene_Type_y Entrez_Gene_Prefix \n", + "0 PR_P07099 protein-coding NCBIGene_2052 \n", + "1 PR_P07099 protein-coding NCBIGene_2052 \n", + "2 PR_P07099 protein-coding NCBIGene_2052 \n", + "\n", + "[3 rows x 24 columns]" + ] + }, + "execution_count": 439, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# merge identifier maps\n", + "ctd_gene_inx = ctd_gene_inx.merge(mesh_chebi_map, left_on='ChemicalID', right_on='MESH_ID')\n", + "ctd_gene_inx = ctd_gene_inx.merge(rna_map, left_on='GeneID', right_on='Entrez_Gene_IDs')\n", + "ctd_gene_inx = ctd_gene_inx.merge(entrez_pro_map, left_on='GeneID', right_on='Gene_IDs')\n", + "\n", + "# visualize data\n", + "ctd_gene_inx.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Create Metadata Dictionary*" + ] + }, + { + "cell_type": "code", + "execution_count": 440, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 8165729/8165729 [1:39:33<00:00, 1366.94it/s] \n" + ] + } + ], + "source": [ + "master_metadata_dictionary['edges'] = {'chemical-gene': {}, 'chemical-rna': {}, 'chemical-protein': {}}\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(ctd_gene_inx.iterrows(), total=ctd_gene_inx.shape[0]):\n", + " chebi = row['CHEBI_ID'].rstrip(); gene_form = None\n", + " chemical_name = row['# ChemicalName']; chemical_id = row['ChemicalID'].rstrip(); casrn = row['CasRN']\n", + " \n", + " evidence = [{'CTD_Interaction': row['Interaction'],\n", + " 'CTD_InteractionActions': row['InteractionActions'],\n", + " 'CTD_PubMedIDs': row['PubMedIDs']}]\n", + " if row['GeneForms'] == 'gene':\n", + " node_key = row['Entrez_Gene_prefix'].rstrip(); gene_form = row['GeneForms']\n", + " if node_key in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[node_key]\n", + " else: genomic_info_dict = None\n", + " edge_key = '{}-{}'.format(chebi, node_key); edge_type = 'chemical-gene'\n", + " if row['GeneForms'] == 'protein':\n", + " node_key = row['Protein_Ontology_IDs'].rstrip(); gene_form = row['GeneForms']\n", + " if node_key in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[node_key]\n", + " else: genomic_info_dict = None\n", + " edge_key = '{}-{}'.format(chebi, node_key); edge_type = 'chemical-protein'\n", + " if row['GeneForms'] == 'mRNA':\n", + " node_key = row['Ensembl_Transcript_IDs'].rstrip(); gene_form = row['GeneForms']\n", + " if node_key in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[node_key]\n", + " else: genomic_info_dict = None\n", + " edge_key = '{}-{}'.format(chebi, node_key); edge_type = 'chemical-rna'\n", + " if gene_form is not None:\n", + " # add chebi metadata\n", + " if chebi in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][chebi].keys():\n", + " master_metadata_dictionary['nodes'][chebi][url]['CTD_ChemicalName'] |= {chemical_name}\n", + " master_metadata_dictionary['nodes'][chebi][url]['CTD_ChemicalID'] |= {chemical_id}\n", + " master_metadata_dictionary['nodes'][chebi][url]['CTD_CasRN'] |= {casrn}\n", + " else:\n", + " master_metadata_dictionary['nodes'][chebi].update({\n", + " url: {'CTD_ChemicalID': {chemical_id},\n", + " 'CTD_CasRN': {casrn},\n", + " 'CTD_ChemicalName': {chemical_name}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({chebi: {\n", + " url: {'CTD_ChemicalID': {chemical_id},\n", + " 'CTD_CasRN': {casrn},\n", + " 'CTD_ChemicalName': {chemical_name}}}})\n", + " \n", + " # add genomic information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][node_key].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][node_key].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", + "\n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'CTD_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['CTD_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['CTD_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'CTD_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'CTD_Evidence': evidence}}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `CTD_chem_go_enriched.tsv` <a class=\"anchor\" id=\"chemical-go\"></a>\n", + "\n", + "**Edges:** \n", + "- `chemical-gobp` \n", + "- `chemical-gocc` \n", + "- `chemical-gomf` \n", + "\n", + "**Identifier Maps:** \n", + "- Chemicals: [MESH_CHEBI_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/MESH_CHEBI_MAP.txt) \n", + "\n", + "This chunk process the [`CTD_chem_go_enriched.tsv`](http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `ChemicalID`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, MeSH Identifier. Variable is provided as a string without a prefix. \n", + " - `CasRN`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, a string containing a CAS Registry Number, if available. \n", + " - `ChemicalName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. If not, a string containing the name of the chemical. \n", + " - `GOTermName`: A string containing the concept's synonym. \n", + " - `Ontology`: A string naming the GO Ontology subset. \n", + "- **Edges:** \n", + " - `HighestGOLevel`: The highest level to which the GO term is assigned within the GO hierarchical ontology. Many GO terms are located at multiple levels within the ontology; only the highest level is displayed. Level 1 constitutes “children” of the most general Biological Process, Cellular Component, and Molecular Function terms. Source: http://ctdbase.org/help/chemGODetailHelp.jsp. \n", + " - `Pvalue`: Raw P-value. Source: http://ctdbase.org/help/chemGODetailHelp.jsp. \n", + " - `CorrectedPValue`:The corrected p-value calculated using the Bonferroni multiple testing adjustment. Source: http://ctdbase.org/help/chemGODetailHelp.jsp. \n", + " - `TargetMatchQty`: The count of matches to the target. Source: http://ctdbase.org/help/chemGODetailHelp.jsp. \n", + " - `TargetTotalQty`: The total matches to the target. Source: http://ctdbase.org/help/chemGODetailHelp.jsp.\n", + " - `BackgroundMatchQty`: The count of matches to the genome. Source: http://ctdbase.org/help/chemGODetailHelp.jsp.\n", + " - `BackgroundTotalQty`: The total matches to the genome. Source: http://ctdbase.org/help/chemGODetailHelp.jsp." + ] + }, + { + "cell_type": "code", + "execution_count": 441, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'CTD_chem_go_enriched.tsv'):\n", + " data_downloader(url, unprocessed_data_location, 'CTD_chem_go_enriched.tsv')\n", + "\n", + "# load data\n", + "ctd_chem_go = pandas.read_csv(unprocessed_data_location + 'CTD_chem_go_enriched.tsv', header=0, delimiter='\\t', skiprows=27)\n", + "ctd_chem_go = ctd_chem_go[ctd_chem_go['# ChemicalName'] != '#']\n", + "ctd_chem_go.fillna('None', inplace=True)\n", + "# update prefix\n", + "ctd_chem_go['ChemicalID'] = 'MESH:' + ctd_chem_go['ChemicalID']\n", + "ctd_chem_go['GOTermID'] = ctd_chem_go['GOTermID'].str.replace(':', '_')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " *Merge Identifier Maps*" + ] + }, + { + "cell_type": "code", + "execution_count": 442, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th># ChemicalName</th>\n", + " <th>ChemicalID</th>\n", + " <th>CasRN</th>\n", + " <th>Ontology</th>\n", + " <th>GOTermName</th>\n", + " <th>GOTermID</th>\n", + " <th>HighestGOLevel</th>\n", + " <th>PValue</th>\n", + " <th>CorrectedPValue</th>\n", + " <th>TargetMatchQty</th>\n", + " <th>TargetTotalQty</th>\n", + " <th>BackgroundMatchQty</th>\n", + " <th>BackgroundTotalQty</th>\n", + " <th>MESH_ID</th>\n", + " <th>CHEBI_ID</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>1,10-phenanthroline</td>\n", + " <td>MESH:C025205</td>\n", + " <td>66-71-7</td>\n", + " <td>Biological Process</td>\n", + " <td>ADP metabolic process</td>\n", + " <td>GO_0046031</td>\n", + " <td>7.0</td>\n", + " <td>1.720000e-21</td>\n", + " <td>7.570000e-18</td>\n", + " <td>13.0</td>\n", + " <td>81.0</td>\n", + " <td>92.0</td>\n", + " <td>44536.0</td>\n", + " <td>MESH:C025205</td>\n", + " <td>CHEBI_44975</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>1,10-phenanthroline</td>\n", + " <td>MESH:C025205</td>\n", + " <td>66-71-7</td>\n", + " <td>Biological Process</td>\n", + " <td>aging</td>\n", + " <td>GO_0007568</td>\n", + " <td>2.0</td>\n", + " <td>5.600000e-16</td>\n", + " <td>2.460000e-12</td>\n", + " <td>14.0</td>\n", + " <td>81.0</td>\n", + " <td>310.0</td>\n", + " <td>44536.0</td>\n", + " <td>MESH:C025205</td>\n", + " <td>CHEBI_44975</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>1,10-phenanthroline</td>\n", + " <td>MESH:C025205</td>\n", + " <td>66-71-7</td>\n", + " <td>Biological Process</td>\n", + " <td>alcohol metabolic process</td>\n", + " <td>GO_0006066</td>\n", + " <td>3.0</td>\n", + " <td>1.000000e-12</td>\n", + " <td>4.410000e-09</td>\n", + " <td>12.0</td>\n", + " <td>81.0</td>\n", + " <td>330.0</td>\n", + " <td>44536.0</td>\n", + " <td>MESH:C025205</td>\n", + " <td>CHEBI_44975</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " # ChemicalName ChemicalID CasRN Ontology \\\n", + "0 1,10-phenanthroline MESH:C025205 66-71-7 Biological Process \n", + "1 1,10-phenanthroline MESH:C025205 66-71-7 Biological Process \n", + "2 1,10-phenanthroline MESH:C025205 66-71-7 Biological Process \n", + "\n", + " GOTermName GOTermID HighestGOLevel PValue \\\n", + "0 ADP metabolic process GO_0046031 7.0 1.720000e-21 \n", + "1 aging GO_0007568 2.0 5.600000e-16 \n", + "2 alcohol metabolic process GO_0006066 3.0 1.000000e-12 \n", + "\n", + " CorrectedPValue TargetMatchQty TargetTotalQty BackgroundMatchQty \\\n", + "0 7.570000e-18 13.0 81.0 92.0 \n", + "1 2.460000e-12 14.0 81.0 310.0 \n", + "2 4.410000e-09 12.0 81.0 330.0 \n", + "\n", + " BackgroundTotalQty MESH_ID CHEBI_ID \n", + "0 44536.0 MESH:C025205 CHEBI_44975 \n", + "1 44536.0 MESH:C025205 CHEBI_44975 \n", + "2 44536.0 MESH:C025205 CHEBI_44975 " + ] + }, + "execution_count": 442, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# merge identifier maps\n", + "ctd_chem_go = ctd_chem_go.merge(mesh_chebi_map, left_on='ChemicalID', right_on='MESH_ID')\n", + "\n", + "# visualize data\n", + "ctd_chem_go.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Create Metadata Dictionary*" + ] + }, + { + "cell_type": "code", + "execution_count": 443, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 3910803/3910803 [29:32<00:00, 2205.92it/s] \n" + ] + } + ], + "source": [ + "master_metadata_dictionary['edges'].update({'chemical-gobp': {}, 'chemical-gocc': {}, 'chemical-gomf': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(ctd_chem_go.iterrows(), total=ctd_chem_go.shape[0]):\n", + " chebi = row['CHEBI_ID'].rstrip(); node_key = row['GOTermID']\n", + " chemical_name = row['# ChemicalName']; chemical_id = row['ChemicalID'].rstrip(); casrn = row['CasRN']\n", + " ontology = row['Ontology']; go_name = row['GOTermName']\n", + " evidence = [{'CTD_Pvalue': row['PValue'],\n", + " 'CTD_CorrectedPValue': row['CorrectedPValue'],\n", + " 'CTD_TargetMatchQty': row['TargetMatchQty'],\n", + " 'CTD_TargetTotalQty': row['TargetTotalQty'],\n", + " 'CTD_BackgroundMatchQty': row['BackgroundMatchQty'],\n", + " 'CTD_BackgroundTotalQty': row['BackgroundTotalQty'],\n", + " 'CTD_HighestGOLevel': row['HighestGOLevel']}]\n", + " # specify edge type, which is related to the ontology aspect\n", + " if ontology == 'Biological Process': edge_key = '{}-{}'.format(chebi, node_key); edge_type = 'chemical-gobp'\n", + " if ontology == 'Cellular Component': edge_key = '{}-{}'.format(chebi, node_key); edge_type = 'chemical-gocc'\n", + " if ontology == 'Molecular Function': edge_key = '{}-{}'.format(chebi, node_key); edge_type = 'chemical-gomf' \n", + " \n", + " # add chebi metadata\n", + " if chebi in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][chebi].keys():\n", + " master_metadata_dictionary['nodes'][chebi][url]['CTD_ChemicalName'] |= {chemical_name}\n", + " master_metadata_dictionary['nodes'][chebi][url]['CTD_ChemicalID'] |= {chemical_id}\n", + " master_metadata_dictionary['nodes'][chebi][url]['CTD_CasRN'] |= {casrn}\n", + " else:\n", + " master_metadata_dictionary['nodes'][chebi].update({\n", + " url: {'CTD_ChemicalID': {chemical_id},\n", + " 'CTD_CasRN': {casrn},\n", + " 'CTD_ChemicalName': {chemical_name}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({chebi: {\n", + " url: {'CTD_ChemicalID': {chemical_id},\n", + " 'CTD_CasRN': {casrn},\n", + " 'CTD_ChemicalName': {chemical_name}}}})\n", + " \n", + " # add go information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key].keys():\n", + " master_metadata_dictionary['nodes'][node_key][url]['CTD_Ontology'] |= {ontology}\n", + " master_metadata_dictionary['nodes'][node_key][url]['CTD_GOTermName'] |= {go_name}\n", + " else:\n", + " master_metadata_dictionary['nodes'][node_key].update({\n", + " url: {'CTD_Ontology': {ontology},\n", + " 'CTD_GOTermName': {go_name}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({node_key: {\n", + " url: {'CTD_Ontology': {ontology},\n", + " 'CTD_GOTermName': {go_name}}}})\n", + " \n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'CTD_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['CTD_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['CTD_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'CTD_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'CTD_Evidence': evidence}} \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `CTD_chemicals_diseases.tsv` <a class=\"anchor\" id=\"chemical-disease\"></a>\n", + "\n", + "**Edges:** \n", + "- `chemical-disease` \n", + "- `chemical-phenotype` \n", + "\n", + "**Identifier Maps:** \n", + "- Chemicals: [MESH_CHEBI_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/MESH_CHEBI_MAP.txt) \n", + "- Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt) \n", + "- Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) \n", + "\n", + "This chunk process the [`CTD_chemicals_diseases.tsv`](http://ctdbase.org/reports/CTD_chemicals_diseases.tsv.gz) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `ChemicalID`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, MeSH Identifier. Variable is provided as a string without a prefix. \n", + " - `CasRN`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, a string containing a CAS Registry Number, if available. \n", + " - `ChemicalName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. If not, a string containing the name of the chemical. \n", + " - `DiseaseName`: A string containing the concept's synonym. \n", + " - `DiseaseID`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", + " - `OmimIDs`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", + "- **Edges:** \n", + " - `DirectEvidence`: '|'-delimited list of strings that include keywords. \n", + " - `InferenceScore`: The inference score (float) reflects the degree of similarity between CTD chemical–gene–disease networks and a similar scale-free random network. The higher the score, the more likely the inference network has atypical connectivity. \n", + " - `PubMedIDs`: |'-delimited list of PubMed identifiers that do not include a prefix. \n", + " - `InferenceGeneSymbol`: A string containing the gene symbol. The genes on which the inferred association is based (i.e., genes that have curated interactions with the chemical and curated associations with the disease). " + ] + }, + { + "cell_type": "code", + "execution_count": 444, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'http://ctdbase.org/reports/CTD_chemicals_diseases.tsv.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'CTD_chemicals_diseases.tsv'):\n", + " data_downloader(url, unprocessed_data_location, 'CTD_chemicals_diseases.tsv')\n", + "\n", + "# load data\n", + "ctd_chem_dis = pandas.read_csv(unprocessed_data_location + 'CTD_chemicals_diseases.tsv', header=0, delimiter='\\t', skiprows=27)\n", + "ctd_chem_dis = ctd_chem_dis[ctd_chem_dis['# ChemicalName'] != '#']\n", + "ctd_chem_dis = ctd_chem_dis[ctd_chem_dis['PubMedIDs'] != numpy.nan]\n", + "ctd_chem_dis = ctd_chem_dis[ctd_chem_dis['DiseaseID'] != numpy.nan]\n", + "# update prefix\n", + "ctd_chem_dis['ChemicalID'] = 'MESH:' + ctd_chem_dis ['ChemicalID']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Merge Identifier Maps*" + ] + }, + { + "cell_type": "code", + "execution_count": 445, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th># ChemicalName</th>\n", + " <th>ChemicalID</th>\n", + " <th>CasRN</th>\n", + " <th>DiseaseName</th>\n", + " <th>DiseaseID</th>\n", + " <th>DirectEvidence</th>\n", + " <th>InferenceGeneSymbol</th>\n", + " <th>InferenceScore</th>\n", + " <th>OmimIDs</th>\n", + " <th>PubMedIDs</th>\n", + " <th>MESH_ID</th>\n", + " <th>CHEBI_ID</th>\n", + " <th>Disease_IDs_x</th>\n", + " <th>MONDO_IDs</th>\n", + " <th>Disease_IDs_y</th>\n", + " <th>HP_IDs</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", + " <td>MESH:C004822</td>\n", + " <td>35079-97-1</td>\n", + " <td>Carcinoma</td>\n", + " <td>MESH:D002277</td>\n", + " <td>NaN</td>\n", + " <td>EPHX1</td>\n", + " <td>5.06</td>\n", + " <td>NaN</td>\n", + " <td>12376462</td>\n", + " <td>MESH:C004822</td>\n", + " <td>CHEBI_4592</td>\n", + " <td>MESH:D002277</td>\n", + " <td>MONDO_0006406</td>\n", + " <td>MESH:D002277</td>\n", + " <td>HP_0030731</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", + " <td>MESH:C004822</td>\n", + " <td>35079-97-1</td>\n", + " <td>Carcinoma</td>\n", + " <td>MESH:D002277</td>\n", + " <td>NaN</td>\n", + " <td>EPHX1</td>\n", + " <td>5.06</td>\n", + " <td>NaN</td>\n", + " <td>12376462</td>\n", + " <td>MESH:C004822</td>\n", + " <td>CHEBI_4592</td>\n", + " <td>MESH:D002277</td>\n", + " <td>MONDO_0004993</td>\n", + " <td>MESH:D002277</td>\n", + " <td>HP_0030731</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>10,11-dihydro-10-hydroxycarbamazepine</td>\n", + " <td>MESH:C039775</td>\n", + " <td>NaN</td>\n", + " <td>Carcinoma</td>\n", + " <td>MESH:D002277</td>\n", + " <td>NaN</td>\n", + " <td>ABCB1</td>\n", + " <td>4.24</td>\n", + " <td>NaN</td>\n", + " <td>21332314</td>\n", + " <td>MESH:C039775</td>\n", + " <td>CHEBI_701</td>\n", + " <td>MESH:D002277</td>\n", + " <td>MONDO_0006406</td>\n", + " <td>MESH:D002277</td>\n", + " <td>HP_0030731</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " # ChemicalName ChemicalID \\\n", + "0 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", + "1 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", + "2 10,11-dihydro-10-hydroxycarbamazepine MESH:C039775 \n", + "\n", + " CasRN DiseaseName DiseaseID DirectEvidence InferenceGeneSymbol \\\n", + "0 35079-97-1 Carcinoma MESH:D002277 NaN EPHX1 \n", + "1 35079-97-1 Carcinoma MESH:D002277 NaN EPHX1 \n", + "2 NaN Carcinoma MESH:D002277 NaN ABCB1 \n", + "\n", + " InferenceScore OmimIDs PubMedIDs MESH_ID CHEBI_ID Disease_IDs_x \\\n", + "0 5.06 NaN 12376462 MESH:C004822 CHEBI_4592 MESH:D002277 \n", + "1 5.06 NaN 12376462 MESH:C004822 CHEBI_4592 MESH:D002277 \n", + "2 4.24 NaN 21332314 MESH:C039775 CHEBI_701 MESH:D002277 \n", + "\n", + " MONDO_IDs Disease_IDs_y HP_IDs \n", + "0 MONDO_0006406 MESH:D002277 HP_0030731 \n", + "1 MONDO_0004993 MESH:D002277 HP_0030731 \n", + "2 MONDO_0006406 MESH:D002277 HP_0030731 " + ] + }, + "execution_count": 445, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ctd_chem_dis = ctd_chem_dis.merge(mesh_chebi_map, left_on='ChemicalID', right_on='MESH_ID')\n", + "ctd_chem_dis = ctd_chem_dis.merge(disease_maps, left_on='DiseaseID', right_on='Disease_IDs')\n", + "ctd_chem_dis = ctd_chem_dis.merge(phenotype_maps, left_on='DiseaseID', right_on='Disease_IDs')\n", + "\n", + "# visualize data\n", + "ctd_chem_dis.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Create Metadata Dictionary*" + ] + }, + { + "cell_type": "code", + "execution_count": 446, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 5127731/5127731 [1:19:02<00:00, 1081.22it/s]\n" + ] + } + ], + "source": [ + "master_metadata_dictionary['edges'].update({'chemical-disease': {}, 'chemical-phenotype': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(ctd_chem_dis.iterrows(), total=ctd_chem_dis.shape[0]):\n", + " chebi = row['CHEBI_ID'].rstrip()\n", + " chemical_name = row['# ChemicalName']; chemical_id = row['ChemicalID'].rstrip(); casrn = row['CasRN']\n", + " dis_name = row['DiseaseName']; dis_id = row['DiseaseID']\n", + " omim = row['OmimIDs'] if not pandas.isna(row['OmimIDs']) else 'None'\n", + " evidence = [{'CTD_DirectEvidence': row['DirectEvidence'] if not pandas.isna(row['DirectEvidence']) else 'None',\n", + " 'CTD_InferenceScore': row['InferenceScore'] if not pandas.isna(row['InferenceScore']) else 'None',\n", + " 'CTD_PubMedIDs': row['PubMedIDs'],\n", + " 'CTD_InferenceGeneSymbol': row['InferenceGeneSymbol'] if not pandas.isna(row['InferenceGeneSymbol']) else 'None'}]\n", + " for node_key in [row['MONDO_IDs'], row['HP_IDs']]:\n", + " if not pandas.isna(node_key) and node_key.startswith('MONDO'):\n", + " edge_key = '{}-{}'.format(chebi, node_key); edge_type = 'chemical-disease'\n", + " if not pandas.isna(node_key) and node_key.startswith('HP'):\n", + " edge_key = '{}-{}'.format(chebi, node_key); edge_type = 'chemical-phenotype'\n", + " \n", + " # add chebi metadata\n", + " if chebi in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][chebi].keys():\n", + " master_metadata_dictionary['nodes'][chebi][url]['CTD_ChemicalName'] |= {chemical_name}\n", + " master_metadata_dictionary['nodes'][chebi][url]['CTD_ChemicalID'] |= {chemical_id}\n", + " master_metadata_dictionary['nodes'][chebi][url]['CTD_CasRN'] |= {casrn}\n", + " else:\n", + " master_metadata_dictionary['nodes'][chebi].update({\n", + " url: {'CTD_ChemicalID': {chemical_id},\n", + " 'CTD_CasRN': {casrn},\n", + " 'CTD_ChemicalName': {chemical_name}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({chebi: {\n", + " url: {'CTD_ChemicalID': {chemical_id},\n", + " 'CTD_CasRN': {casrn},\n", + " 'CTD_ChemicalName': {chemical_name}}}})\n", + " \n", + " # add disease information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key]:\n", + " master_metadata_dictionary['nodes'][node_key][url]['CTD_DiseaseName'] |= {dis_name}\n", + " master_metadata_dictionary['nodes'][node_key][url]['CTD_DiseaseID'] |= {dis_id}\n", + " master_metadata_dictionary['nodes'][node_key][url]['CTD_OmimIDs'] |= {omim}\n", + " else:\n", + " master_metadata_dictionary['nodes'][node_key].update({\n", + " url: {'CTD_DiseaseName': {dis_name},\n", + " 'CTD_DiseaseID': {dis_id},\n", + " 'CTD_OmimIDs': {omim}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({node_key: {\n", + " url: {'CTD_DiseaseName': {dis_name},\n", + " 'CTD_DiseaseID': {dis_id},\n", + " 'CTD_OmimIDs': {omim}}}})\n", + " \n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'CTD_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['CTD_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['CTD_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'CTD_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'CTD_Evidence': evidence}}\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `ChEBI2Reactome_All_Levels.txt` <a class=\"anchor\" id=\"gene-pathway\"></a>\n", + "\n", + "\n", + "**Edges:** \n", + "- `chemical-pathway` \n", + "\n", + "This chunk process the [`ChEBI2Reactome_All_Levels.txt`](https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt) file and obtains the following node metadata: \n", + "- **Nodes:** \n", + " - `DBReference`: A string containing the concept's database cross-reference, which is formatted as prefix:ID. \n", + "- **Edges:** \n", + " - `EvidenceID`: A string containing an evidence code." + ] + }, + { + "cell_type": "code", + "execution_count": 447, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>0</th>\n", + " <th>1</th>\n", + " <th>2</th>\n", + " <th>3</th>\n", + " <th>4</th>\n", + " <th>5</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>16</th>\n", + " <td>CHEBI_10033</td>\n", + " <td>reactome_R-HSA-1430728</td>\n", + " <td>https://reactome.org/PathwayBrowser/#/R-HSA-14...</td>\n", + " <td>Metabolism</td>\n", + " <td>TAS</td>\n", + " <td>Homo sapiens</td>\n", + " </tr>\n", + " <tr>\n", + " <th>17</th>\n", + " <td>CHEBI_10033</td>\n", + " <td>reactome_R-HSA-196854</td>\n", + " <td>https://reactome.org/PathwayBrowser/#/R-HSA-19...</td>\n", + " <td>Metabolism of vitamins and cofactors</td>\n", + " <td>TAS</td>\n", + " <td>Homo sapiens</td>\n", + " </tr>\n", + " <tr>\n", + " <th>18</th>\n", + " <td>CHEBI_10033</td>\n", + " <td>reactome_R-HSA-6806664</td>\n", + " <td>https://reactome.org/PathwayBrowser/#/R-HSA-68...</td>\n", + " <td>Metabolism of vitamin K</td>\n", + " <td>TAS</td>\n", + " <td>Homo sapiens</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " 0 1 \\\n", + "16 CHEBI_10033 reactome_R-HSA-1430728 \n", + "17 CHEBI_10033 reactome_R-HSA-196854 \n", + "18 CHEBI_10033 reactome_R-HSA-6806664 \n", + "\n", + " 2 \\\n", + "16 https://reactome.org/PathwayBrowser/#/R-HSA-14... \n", + "17 https://reactome.org/PathwayBrowser/#/R-HSA-19... \n", + "18 https://reactome.org/PathwayBrowser/#/R-HSA-68... \n", + "\n", + " 3 4 5 \n", + "16 Metabolism TAS Homo sapiens \n", + "17 Metabolism of vitamins and cofactors TAS Homo sapiens \n", + "18 Metabolism of vitamin K TAS Homo sapiens " + ] + }, + "execution_count": 447, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# download data\n", + "url = 'https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'ChEBI2Reactome_All_Levels.txt'):\n", + " data_downloader(url, unprocessed_data_location, 'ChEBI2Reactome_All_Levels.txt')\n", + "\n", + "# load data\n", + "rtm_chem_path = pandas.read_csv(unprocessed_data_location + 'ChEBI2Reactome_All_Levels.txt', header=None, delimiter='\\t', skiprows=0)\n", + "rtm_chem_path = rtm_chem_path[rtm_chem_path[5] == 'Homo sapiens']\n", + "rtm_chem_path.fillna('None', inplace=True)\n", + "# update prefix\n", + "rtm_chem_path[0] = 'CHEBI_' + rtm_chem_path[0].astype('str')\n", + "rtm_chem_path[1] = 'reactome_' + rtm_chem_path[1]\n", + "\n", + "# visualize data\n", + "rtm_chem_path.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Create Metadata Dictionary*" + ] + }, + { + "cell_type": "code", + "execution_count": 448, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 33628/33628 [00:10<00:00, 3269.54it/s]\n" + ] + } + ], + "source": [ + "master_metadata_dictionary['edges'].update({'chemical-pathway': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(rtm_chem_path.iterrows(), total=rtm_chem_path.shape[0]):\n", + " chebi = row[0].rstrip(); node_key = row[1]; path_name = row[3]\n", + " evidence = [{'CTD_EvidenceID': row[4]}] \n", + " edge_key = '{}-{}'.format(chebi, node_key); edge_type = 'chemical-pathway' \n", + " \n", + " # add reactome information \n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key].keys():\n", + " master_metadata_dictionary['nodes'][node_key][url]['Reactome_PathwayName'] |= {path_name}\n", + " else:\n", + " master_metadata_dictionary['nodes'][node_key].update({\n", + " url: {'Reactome_PathwayName': {path_name}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({node_key: {\n", + " url: {'Reactome_PathwayName': {path_name}}}})\n", + " \n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'Reactome_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['Reactome_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Reactome_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Reactome_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Reactome_Evidence': evidence}}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `goa_human.gaf` <a class=\"anchor\" id=\"goa\"></a>\n", + "\n", + "**Edges:** \n", + "- `protein-gobp` \n", + "- `protein-gocc` \n", + "- `protein-gomf` \n", + "\n", + "**Identifier Maps:** \n", + "- Proteins: [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", + "This chunk process the [`goa_human.gaf`](http://current.geneontology.org/annotations/goa_human.gaf.gz) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `Aspect`: A variable that indicates what species the annotation applies to. \n", + " - `DB_Object_Name`: A string containing the concept's synonym. \n", + " - `DB_Object_Synonym`: A string containing the concept's synonym. \n", + " - `DB_Object_Symbol`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", + " - `With_Or_From`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", + " - `DB_Object_Type`: A string indicating the type of object that has been annotated. \n", + "- **Edges:** \n", + " - `Qualifier`: Some annotations are modified by qualifiers, which have specific usage rules and meanings within GO. \n", + " - `DB_Reference`: One or more unique identifiers for a single source cited as an authority for the attribution of the GO ID to the DB Object ID. This may be a literature reference or a database record. The syntax is DB:accession_number. \n", + " - `EvidenceCode`: Each annotation includes an evidence code to indicate how the annotation to a particular term is supported\n", + " - Inferred from Experiment (EXP)\n", + " - Inferred from Direct Assay (IDA)\n", + " - Inferred from Physical Interaction (IPI)\n", + " - Inferred from Mutant Phenotype (IMP)\n", + " - Inferred from Genetic Interaction (IGI)\n", + " - Inferred from Expression Pattern (IEP)\n", + " - Inferred from High Throughput Experiment (HTP)\n", + " - Inferred from High Throughput Direct Assay (HDA)\n", + " - Inferred from High Throughput Mutant Phenotype (HMP)\n", + " - Inferred from High Throughput Genetic Interaction (HGI)\n", + " - Inferred from High Throughput Expression Pattern (HEP)\n", + " - Inferred from Biological aspect of Ancestor (IBA)\n", + " - Inferred from Biological aspect of Descendant (IBD)\n", + " - Inferred from Key Residues (IKR)\n", + " - Inferred from Rapid Divergence (IRD)\n", + " - Inferred from Sequence or structural Similarity (ISS)\n", + " - Inferred from Sequence Orthology (ISO)\n", + " - Inferred from Sequence Alignment (ISA)\n", + " - Inferred from Sequence Model (ISM)\n", + " - Inferred from Genomic Context (IGC)\n", + " - Inferred from Reviewed Computational Analysis (RCA)\n", + " - Traceable Author Statement (TAS)\n", + " - Non-traceable Author Statement (NAS)\n", + " - Inferred by Curator (IC)\n", + " - No biological Data available (ND)\n", + " - Inferred from Electronic Annotation (IEA) \n", + " - `AssignedBy`: A string indicating who assigned the association. " + ] + }, + { + "cell_type": "code", + "execution_count": 449, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'http://current.geneontology.org/annotations/goa_human.gaf.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'goa_human.gaf'):\n", + " data_downloader(url, unprocessed_data_location, 'goa_human.gaf')\n", + "\n", + "# load data\n", + "goa_gene = pandas.read_csv(unprocessed_data_location + 'goa_human.gaf', header=None, delimiter='\\t', skiprows=41, low_memory=False)\n", + "goa_gene = goa_gene[goa_gene[12] == 'taxon:9606']\n", + "goa_gene = goa_gene[goa_gene[3] != 'NOT']\n", + "goa_gene = goa_gene[goa_gene[11] == 'protein']\n", + "# fix prefix\n", + "goa_gene[4] = goa_gene[4].str.replace(':', '_')\n", + "goa_gene.fillna('None', inplace=True)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " *Merge Identifier Maps*" + ] + }, + { + "cell_type": "code", + "execution_count": 450, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>0</th>\n", + " <th>1</th>\n", + " <th>2</th>\n", + " <th>3</th>\n", + " <th>4</th>\n", + " <th>5</th>\n", + " <th>6</th>\n", + " <th>7</th>\n", + " <th>8</th>\n", + " <th>9</th>\n", + " <th>10</th>\n", + " <th>11</th>\n", + " <th>12</th>\n", + " <th>13</th>\n", + " <th>14</th>\n", + " <th>15</th>\n", + " <th>16</th>\n", + " <th>Uniprot_Accession_IDs</th>\n", + " <th>Protein_Ontology_IDs</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>UniProtKB</td>\n", + " <td>A0A024RBG1</td>\n", + " <td>NUDT4B</td>\n", + " <td>enables</td>\n", + " <td>GO_0003723</td>\n", + " <td>GO_REF:0000043</td>\n", + " <td>IEA</td>\n", + " <td>UniProtKB-KW:KW-0694</td>\n", + " <td>F</td>\n", + " <td>Diphosphoinositol polyphosphate phosphohydrola...</td>\n", + " <td>NUDT4B</td>\n", + " <td>protein</td>\n", + " <td>taxon:9606</td>\n", + " <td>20211010</td>\n", + " <td>UniProt</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>A0A024RBG1</td>\n", + " <td>PR_A0A024RBG1</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>UniProtKB</td>\n", + " <td>A0A024RBG1</td>\n", + " <td>NUDT4B</td>\n", + " <td>enables</td>\n", + " <td>GO_0046872</td>\n", + " <td>GO_REF:0000043</td>\n", + " <td>IEA</td>\n", + " <td>UniProtKB-KW:KW-0479</td>\n", + " <td>F</td>\n", + " <td>Diphosphoinositol polyphosphate phosphohydrola...</td>\n", + " <td>NUDT4B</td>\n", + " <td>protein</td>\n", + " <td>taxon:9606</td>\n", + " <td>20211010</td>\n", + " <td>UniProt</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>A0A024RBG1</td>\n", + " <td>PR_A0A024RBG1</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>UniProtKB</td>\n", + " <td>A0A024RBG1</td>\n", + " <td>NUDT4B</td>\n", + " <td>enables</td>\n", + " <td>GO_0052840</td>\n", + " <td>GO_REF:0000003</td>\n", + " <td>IEA</td>\n", + " <td>EC:3.6.1.52</td>\n", + " <td>F</td>\n", + " <td>Diphosphoinositol polyphosphate phosphohydrola...</td>\n", + " <td>NUDT4B</td>\n", + " <td>protein</td>\n", + " <td>taxon:9606</td>\n", + " <td>20211009</td>\n", + " <td>UniProt</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>A0A024RBG1</td>\n", + " <td>PR_A0A024RBG1</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " 0 1 2 3 4 5 6 \\\n", + "0 UniProtKB A0A024RBG1 NUDT4B enables GO_0003723 GO_REF:0000043 IEA \n", + "1 UniProtKB A0A024RBG1 NUDT4B enables GO_0046872 GO_REF:0000043 IEA \n", + "2 UniProtKB A0A024RBG1 NUDT4B enables GO_0052840 GO_REF:0000003 IEA \n", + "\n", + " 7 8 9 \\\n", + "0 UniProtKB-KW:KW-0694 F Diphosphoinositol polyphosphate phosphohydrola... \n", + "1 UniProtKB-KW:KW-0479 F Diphosphoinositol polyphosphate phosphohydrola... \n", + "2 EC:3.6.1.52 F Diphosphoinositol polyphosphate phosphohydrola... \n", + "\n", + " 10 11 12 13 14 15 16 \\\n", + "0 NUDT4B protein taxon:9606 20211010 UniProt None None \n", + "1 NUDT4B protein taxon:9606 20211010 UniProt None None \n", + "2 NUDT4B protein taxon:9606 20211009 UniProt None None \n", + "\n", + " Uniprot_Accession_IDs Protein_Ontology_IDs \n", + "0 A0A024RBG1 PR_A0A024RBG1 \n", + "1 A0A024RBG1 PR_A0A024RBG1 \n", + "2 A0A024RBG1 PR_A0A024RBG1 " + ] + }, + "execution_count": 450, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "goa_gene = goa_gene.merge(uniprot_pro_map, left_on=1, right_on='Uniprot_Accession_IDs')\n", + "\n", + "# visualize data\n", + "goa_gene.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Create Metadata Dictionary*" + ] + }, + { + "cell_type": "code", + "execution_count": 451, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 613887/613887 [04:24<00:00, 2320.65it/s]\n" + ] + } + ], + "source": [ + "master_metadata_dictionary['edges'].update({'protein-gobp': {}, 'protein-gocc': {}, 'protein-gomf': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(goa_gene.iterrows(), total=goa_gene.shape[0]):\n", + " pr = row['Protein_Ontology_IDs'].rstrip(); node_key = row[4]\n", + " pr_db = row[9]; pr_syn = row[10]; pr_symb = row[2]; aspect = row[8]; db_with = row[7]\n", + " evidence = [{'GOA_Qualifier': row[3], 'GOA_DB_Reference': row[5],\n", + " 'GOA_EvidenceCode': row[6], 'GOA_AssignedBy': row[14]}]\n", + " if aspect == 'P': edge_key = '{}-{}'.format(pr, node_key); edge_type = 'protein-gobp'\n", + " if aspect == 'C': edge_key = '{}-{}'.format(pr, node_key); edge_type = 'protein-gocc'\n", + " if aspect == 'F': edge_key = '{}-{}'.format(pr, node_key); edge_type = 'protein-gomf' \n", + " if pr in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[pr]\n", + " else: genomic_info_dict = None\n", + " \n", + " # add pr information\n", + " if pr in master_metadata_dictionary['nodes'].keys(): \n", + " if url in master_metadata_dictionary['nodes'][pr].keys():\n", + " master_metadata_dictionary['nodes'][pr][url]['GOA_Aspect'] |= {aspect}\n", + " master_metadata_dictionary['nodes'][pr][url]['GOA_DB_Object_Name'] |= {pr_db}\n", + " master_metadata_dictionary['nodes'][pr][url]['GOA_DB_Object_Synonym'] |= {pr_syn}\n", + " master_metadata_dictionary['nodes'][pr][url]['GOA_DB_Object_Symbol'] |= {pr_symb}\n", + " master_metadata_dictionary['nodes'][pr][url]['GOA_With_Or_From'] |= {db_with}\n", + " else:\n", + " master_metadata_dictionary['nodes'][pr].update({\n", + " url: {'GOA_Aspect': {aspect},\n", + " 'GOA_DB_Object_Name': {pr_db},\n", + " 'GOA_DB_Object_Synonym': {pr_syn},\n", + " 'GOA_DB_Object_Symbol': {pr_symb},\n", + " 'GOA_With_Or_From': {db_with}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({pr: {\n", + " url: {'GOA_Aspect': {aspect},\n", + " 'GOA_DB_Object_Name': {pr_db},\n", + " 'GOA_DB_Object_Synonym': {pr_syn},\n", + " 'GOA_DB_Object_Symbol': {pr_symb},\n", + " 'GOA_With_Or_From': {db_with}}}})\n", + " \n", + " # add genomic information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][node_key].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][node_key].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", + "\n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'GOA_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['GOA_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['GOA_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'GOA_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'GOA_Evidence': evidence}}\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt` <a class=\"anchor\" id=\"gene-gene\"></a>\n", + "\n", + "**Edges:** \n", + "- `gene-gene` \n", + "\n", + "**Identifier Maps:** \n", + "- Genes: [UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt) \n", + "\n", + "This chunk process the [`COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt`](http://genemania.org/data/current/Homo_sapiens.COMBINED/COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt) file and obtains the following edge metadata: \n", + "- **Edges:** \n", + " - `Weight`: Assumes the input gene list is related through GO biological processes. The score will vary depending on the type of network, but in general is a number ranging from zero (no interaction) to 1 (strong interaction). See PMID: 25254104 for more detail. " + ] + }, + { + "cell_type": "code", + "execution_count": 459, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'http://genemania.org/data/current/Homo_sapiens.COMBINED/COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt'):\n", + " data_downloader(url, unprocessed_data_location, 'COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt')\n", + "\n", + "# load data\n", + "gm_gene_gene = pandas.read_csv(unprocessed_data_location + 'COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt', header=0, delimiter='\\t', skiprows=0)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " *Merge Identifier Maps*" + ] + }, + { + "cell_type": "code", + "execution_count": 460, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Gene_A</th>\n", + " <th>Gene_B</th>\n", + " <th>Weight</th>\n", + " <th>Uniprot_Accession_IDs_x</th>\n", + " <th>Entrez_Gene_A</th>\n", + " <th>master_gene_type_x</th>\n", + " <th>gene_type_update_x</th>\n", + " <th>Uniprot_Accession_IDs_y</th>\n", + " <th>Entrez_Gene_B</th>\n", + " <th>master_gene_type_y</th>\n", + " <th>gene_type_update_y</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>O60762</td>\n", + " <td>P48506</td>\n", + " <td>2.700000e-05</td>\n", + " <td>O60762</td>\n", + " <td>NCBIGene_8813</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " <td>P48506</td>\n", + " <td>NCBIGene_2729</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>Q8IZE3</td>\n", + " <td>P48506</td>\n", + " <td>6.800000e-08</td>\n", + " <td>Q8IZE3</td>\n", + " <td>NCBIGene_57147</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " <td>P48506</td>\n", + " <td>NCBIGene_2729</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>Q9NSG2</td>\n", + " <td>P48506</td>\n", + " <td>1.100000e-07</td>\n", + " <td>Q9NSG2</td>\n", + " <td>NCBIGene_55732</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " <td>P48506</td>\n", + " <td>NCBIGene_2729</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Gene_A Gene_B Weight Uniprot_Accession_IDs_x Entrez_Gene_A \\\n", + "0 O60762 P48506 2.700000e-05 O60762 NCBIGene_8813 \n", + "1 Q8IZE3 P48506 6.800000e-08 Q8IZE3 NCBIGene_57147 \n", + "2 Q9NSG2 P48506 1.100000e-07 Q9NSG2 NCBIGene_55732 \n", + "\n", + " master_gene_type_x gene_type_update_x Uniprot_Accession_IDs_y \\\n", + "0 protein-coding protein-coding P48506 \n", + "1 protein-coding protein-coding P48506 \n", + "2 protein-coding protein-coding P48506 \n", + "\n", + " Entrez_Gene_B master_gene_type_y gene_type_update_y \n", + "0 NCBIGene_2729 protein-coding protein-coding \n", + "1 NCBIGene_2729 protein-coding protein-coding \n", + "2 NCBIGene_2729 protein-coding protein-coding " + ] + }, + "execution_count": 460, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gm_gene_gene = gm_gene_gene.merge(uniprot_entrez_data, left_on='Gene_A', right_on='Uniprot_Accession_IDs')\n", + "gm_gene_gene.rename(columns={'Entrez_Gene_IDs': 'Entrez_Gene_A'}, inplace=True)\n", + "gm_gene_gene = gm_gene_gene.merge(uniprot_entrez_data, left_on='Gene_B', right_on='Uniprot_Accession_IDs')\n", + "gm_gene_gene.rename(columns={'Entrez_Gene_IDs': 'Entrez_Gene_B'}, inplace=True)\n", + "\n", + "# visualize data\n", + "gm_gene_gene.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Create Metadata Dictionary*" + ] + }, + { + "cell_type": "code", + "execution_count": 461, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 11706791/11706791 [59:03<00:00, 3303.94it/s] \n" + ] + } + ], + "source": [ + "master_metadata_dictionary['edges'].update({'gene-gene': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(gm_gene_gene.iterrows(), total=gm_gene_gene.shape[0]):\n", + " genes = [row['Entrez_Gene_A'], row['Entrez_Gene_B']]; weight = row['Weight']; gene_info = []\n", + " edge_key = '{}-{}'.format(row['Entrez_Gene_A'], row['Entrez_Gene_B']); edge_type = 'gene-gene' \n", + " \n", + " for node_key in genes:\n", + " if node_key in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[node_key]\n", + " else: genomic_info_dict = None\n", + " \n", + " # add genomic information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][node_key].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][node_key].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}}) \n", + " \n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'GeneMania_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['GeneMania_Evidence'] = weight\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'GeneMania_Evidence': weight}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'GeneMania_Evidence': weight}}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `phenotype.hpoa` <a class=\"anchor\" id=\"phenotype-disease\"></a>\n", + "\n", + "**Edges:** \n", + "- `disease-phenotype` \n", + "\n", + "**Identifier Maps:** \n", + "- Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt) \n", + "\n", + "This chunk process the [`phenotype.hpoa`](http://purl.obolibrary.org/obo/hp/hpoa/phenotype.hpoa) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `DiseaseName`: A string containing the concept's synonym. \n", + "- **Edges:** \n", + " - `Reference`: This required field indicates the source of the information used for the annotation. This may be the clinical experience of the annotator or may be taken from an article as indicated by a PubMed id. Each collaborating center of the Human Phenotype Ontology consortium is assigned a HPO:Ref id. In addition, if appropriate, a PubMed id for an article describing the clinical abnormality may be used. \n", + " - `Evidence`: This required field indicates the level of evidence supporting the annotation. Annotations that have been extracted by parsing the Clinical Features sections of the omim.txt file are assigned the evidence code IEA. Other codes include PCS for published clinical study. This should be used for information extracted from articles in the medical literature. ICE can be used for annotations based on individual clinical experience. This may be appropriate for disorders with a limited amount of published data. This must be accompanied by an entry in the DB:Reference field denoting the individual or center performing the annotation together with an identifier. For instance, GH:007 might be used to refer to the seventh such annotation made by a specialist from Gotham Hospital (assuming the prefix GH has been registered with the HPO). Finally we have TAS, which stands for “traceable author statement”, usually reviews or disease entries (e.g. OMIM) that only refers to the original publication.. \n", + " - `Frequency`: A term-id from the HPO-sub-ontology below the term Frequency.\n", + " There are three allowed options for this field.\n", + " 1. A term-id from the HPO-sub-ontology below the term Frequency.\n", + " 2. A count of patients affected within a cohort. For instance, 7/13 would indicate that 7 of the 13 patients with the specified disease were found to have the phenotypic abnormality referred to by the HPO term in question in the study referred to by the DB_Reference\n", + " 3. A percentage value such as 17%, again referring to the percentage of patients found to have the phenotypic abnormality referred to by the HPO term in question in the study referred to by the DB_Reference. If possible, the 7/13 format is preferred over the percentage format if the exact data is available.. \n", + " - `Sex`: This field contains the strings MALE or FEMALE if the annotation in question is limited to males or females. This field refers to the phenotypic (and not the chromosomal) sex, and does not intend to capture the further complexities of sex determination. If a phenotype is limited to one or the other sex, then the corresponding term from the Clinical modifier subontology should also be used in the Modifier field.\n", + " - `Modifier`: A term from the Clinical modifier subontology. \n", + " - `Aspect`: One of P (Phenotypic abnormality), I (inheritance), C (onset and clinical course). This field is mandatory; cardinality 1. \n", + " - `Biocuration`: This refers to the center or user making the annotation and the date on which the annotation was made; format is YYYY-MM-DD this field is mandatory. Multiple entries can be separated by a semicolon if an annotation was revised, e.g., HPO:skoehler[2010-04-21];HPO:lcarmody[2019-06-02]. " + ] + }, + { + "cell_type": "code", + "execution_count": 455, + "metadata": {}, + "outputs": [], + "source": [ + "# download data\n", + "url = 'http://purl.obolibrary.org/obo/hp/hpoa/phenotype.hpoa'\n", + "if not os.path.exists(unprocessed_data_location + 'phenotype.hpoa'):\n", + " data_downloader(url, unprocessed_data_location, 'phenotype.hpoa')\n", + "\n", + "# load data\n", + "hpo_dis_phe = pandas.read_csv(unprocessed_data_location + 'phenotype.hpoa', header=0, delimiter='\\t', skiprows=4, low_memory=False)\n", + "hpo_dis_phe = hpo_dis_phe[hpo_dis_phe['Qualifier'] != 'NOT']\n", + "hpo_dis_phe.fillna('None', inplace=True)\n", + "\n", + "# fix prefix\n", + "hpo_dis_phe['HPO_ID'] = hpo_dis_phe['HPO_ID'].str.replace(':', '_')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "<br>\n", + " *Merge Identifier Maps*" + ] + }, + { + "cell_type": "code", + "execution_count": 456, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>#DatabaseID</th>\n", + " <th>DiseaseName</th>\n", + " <th>Qualifier</th>\n", + " <th>HPO_ID</th>\n", + " <th>Reference</th>\n", + " <th>Evidence</th>\n", + " <th>Onset</th>\n", + " <th>Frequency</th>\n", + " <th>Sex</th>\n", + " <th>Modifier</th>\n", + " <th>Aspect</th>\n", + " <th>Biocuration</th>\n", + " <th>Disease_IDs</th>\n", + " <th>MONDO_IDs</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>OMIM:116860</td>\n", + " <td>Cerebral cavernous malformations 1</td>\n", + " <td>None</td>\n", + " <td>HP_0001250</td>\n", + " <td>OMIM:116860</td>\n", + " <td>IEA</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>P</td>\n", + " <td>HPO:iea[2009-02-17]</td>\n", + " <td>OMIM:116860</td>\n", + " <td>MONDO_0031037</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>OMIM:116860</td>\n", + " <td>Cerebral cavernous malformations 1</td>\n", + " <td>None</td>\n", + " <td>HP_0003011</td>\n", + " <td>OMIM:116860</td>\n", + " <td>IEA</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>P</td>\n", + " <td>HPO:iea[2009-02-17]</td>\n", + " <td>OMIM:116860</td>\n", + " <td>MONDO_0031037</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>OMIM:116860</td>\n", + " <td>Cerebral cavernous malformations 1</td>\n", + " <td>None</td>\n", + " <td>HP_0003829</td>\n", + " <td>OMIM:116860</td>\n", + " <td>IEA</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>M</td>\n", + " <td>HPO:iea[2009-02-17]</td>\n", + " <td>OMIM:116860</td>\n", + " <td>MONDO_0031037</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " #DatabaseID DiseaseName Qualifier HPO_ID \\\n", + "0 OMIM:116860 Cerebral cavernous malformations 1 None HP_0001250 \n", + "1 OMIM:116860 Cerebral cavernous malformations 1 None HP_0003011 \n", + "2 OMIM:116860 Cerebral cavernous malformations 1 None HP_0003829 \n", + "\n", + " Reference Evidence Onset Frequency Sex Modifier Aspect \\\n", + "0 OMIM:116860 IEA None None None None P \n", + "1 OMIM:116860 IEA None None None None P \n", + "2 OMIM:116860 IEA None None None None M \n", + "\n", + " Biocuration Disease_IDs MONDO_IDs \n", + "0 HPO:iea[2009-02-17] OMIM:116860 MONDO_0031037 \n", + "1 HPO:iea[2009-02-17] OMIM:116860 MONDO_0031037 \n", + "2 HPO:iea[2009-02-17] OMIM:116860 MONDO_0031037 " + ] + }, + "execution_count": 456, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hpo_dis_phe = hpo_dis_phe.merge(disease_maps, left_on='#DatabaseID', right_on='Disease_IDs')\n", "\n", - "[**`disease_names`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names)\n", + "# visualize data\n", + "hpo_dis_phe.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Create Metadata Dictionary*" + ] + }, + { + "cell_type": "code", + "execution_count": 458, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 180689/180689 [01:13<00:00, 2450.57it/s]\n" + ] + } + ], + "source": [ + "master_metadata_dictionary['edges'].update({'disease-phenotype': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(hpo_dis_phe.iterrows(), total=hpo_dis_phe.shape[0]):\n", + " concepts = [row['MONDO_IDs'], row['HPO_ID']]\n", + " disease_names = {row['MONDO_IDs']: {row['DiseaseName']}, row['HPO_ID']: {'None'}}\n", + " edge_key = '{}-{}'.format(row['MONDO_IDs'], row['HPO_ID']); edge_type = 'disease-phenotype'\n", + " evidence = [{'HPO_Reference': row['Reference'],\n", + " 'HPO_EvidenceCode': row['Evidence'],\n", + " 'HPO_Frequency': row['Frequency'],\n", + " 'HPO_Sex': row['Sex'],\n", + " 'HPO_Modifier': row['Modifier'],\n", + " 'HPO_Aspect': row['Aspect'],\n", + " 'HPO_Biocuration': row['Biocuration']}]\n", + " for node_key in concepts:\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key].keys():\n", + " master_metadata_dictionary['nodes'][node_key][url]['HPO_DiseaseName'] |= disease_names[node_key]\n", + " else: master_metadata_dictionary['nodes'][node_key].update({url: {'HPO_DiseaseName': disease_names[node_key]}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key: {url: {'HPO_DiseaseName': disease_names[node_key]}}})\n", + " \n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'HPO_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['HPO_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['HPO_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'HPO_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'HPO_Evidence': evidence}}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", "\n", - "> Tab-delimited file with the following 7 fields:\n", - ">\n", - "> - <u>DiseaseName</u>: The name preferred by GTR and ClinVar \n", - "> - <u>SourceName</u>: Sources that also use this preferred name \n", - "> - <u>ConceptID</u>: The identifier assigned to a disorder associated with this gene. If the value starts with a C and is followed by digits, the ConceptID is a value from UMLS; if a value begins with CN, it was created by NCBI-based processing \n", - "> - <u>SourceID</u>: Identifier used by the source reported in column 2 (SourceName) \n", - "> - <u>DiseaseMIM</u>: MIM number for the condition \n", - "> - <u>LastUpdated</u>: Last time this record was modified by NCBI staff \n", - "> - <u>Category</u>: Category of disease (as reported in ClinVar's XML), one of: \n", - "> - Blood group\n", - "> - Disease\n", - "> - Finding\n", - "> - Named protein variant\n", - "> - Pharmacological response\n", - "> - phenotype instruction" + "#### `gene_association.reactome` <a class=\"anchor\" id=\"reactome-go\"></a>\n", + "\n", + "**Edges:** \n", + "- `gobp-pathway` \n", + "- `pathway-gocc` \n", + "- `pathway-gomf` \n", + "\n", + "This chunk process the [`gene_association.reactome.tsv`](https://reactome.org/download/current/gene_association.reactome.gz) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `DBReference`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", + " - `Aspect`: A variable indicating what GO subontology is being used. \n", + "- **Edges:** \n", + " - `EvidenceCode`: Each annotation includes an evidence code to indicate how the annotation to a particular term is supported\n", + " - Inferred from Experiment (EXP)\n", + " - Inferred from Direct Assay (IDA)\n", + " - Inferred from Physical Interaction (IPI)\n", + " - Inferred from Mutant Phenotype (IMP)\n", + " - Inferred from Genetic Interaction (IGI)\n", + " - Inferred from Expression Pattern (IEP)\n", + " - Inferred from High Throughput Experiment (HTP)\n", + " - Inferred from High Throughput Direct Assay (HDA)\n", + " - Inferred from High Throughput Mutant Phenotype (HMP)\n", + " - Inferred from High Throughput Genetic Interaction (HGI)\n", + " - Inferred from High Throughput Expression Pattern (HEP)\n", + " - Inferred from Biological aspect of Ancestor (IBA)\n", + " - Inferred from Biological aspect of Descendant (IBD)\n", + " - Inferred from Key Residues (IKR)\n", + " - Inferred from Rapid Divergence (IRD)\n", + " - Inferred from Sequence or structural Similarity (ISS)\n", + " - Inferred from Sequence Orthology (ISO)\n", + " - Inferred from Sequence Alignment (ISA)\n", + " - Inferred from Sequence Model (ISM)\n", + " - Inferred from Genomic Context (IGC)\n", + " - Inferred from Reviewed Computational Analysis (RCA)\n", + " - Traceable Author Statement (TAS)\n", + " - Non-traceable Author Statement (NAS)\n", + " - Inferred by Curator (IC)\n", + " - No biological Data available (ND)\n", + " - Inferred from Electronic Annotation (IEA) \n", + " - `AssignedBy`: A string indicating who assigned the association. \n", + " - `Qualifier`: Some annotations are modified by qualifiers, which have specific usage rules and meanings within GO. " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>0</th>\n", + " <th>1</th>\n", + " <th>2</th>\n", + " <th>3</th>\n", + " <th>4</th>\n", + " <th>5</th>\n", + " <th>6</th>\n", + " <th>7</th>\n", + " <th>8</th>\n", + " <th>9</th>\n", + " <th>10</th>\n", + " <th>11</th>\n", + " <th>12</th>\n", + " <th>13</th>\n", + " <th>14</th>\n", + " <th>15</th>\n", + " <th>16</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>UniProtKB</td>\n", + " <td>A0A075B6P5</td>\n", + " <td>KV228_HUMAN</td>\n", + " <td>located_in</td>\n", + " <td>GO_0005576</td>\n", + " <td>reactome_R-HSA-166753</td>\n", + " <td>TAS</td>\n", + " <td>None</td>\n", + " <td>C</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>protein</td>\n", + " <td>taxon:9606</td>\n", + " <td>20161111</td>\n", + " <td>Reactome</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>UniProtKB</td>\n", + " <td>A0A075B6P5</td>\n", + " <td>KV228_HUMAN</td>\n", + " <td>located_in</td>\n", + " <td>GO_0005576</td>\n", + " <td>reactome_R-HSA-166792</td>\n", + " <td>TAS</td>\n", + " <td>None</td>\n", + " <td>C</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>protein</td>\n", + " <td>taxon:9606</td>\n", + " <td>20161111</td>\n", + " <td>Reactome</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>UniProtKB</td>\n", + " <td>A0A075B6P5</td>\n", + " <td>KV228_HUMAN</td>\n", + " <td>located_in</td>\n", + " <td>GO_0005576</td>\n", + " <td>reactome_R-HSA-173626</td>\n", + " <td>TAS</td>\n", + " <td>None</td>\n", + " <td>C</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " <td>protein</td>\n", + " <td>taxon:9606</td>\n", + " <td>20161111</td>\n", + " <td>Reactome</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " 0 1 2 3 4 \\\n", + "0 UniProtKB A0A075B6P5 KV228_HUMAN located_in GO_0005576 \n", + "1 UniProtKB A0A075B6P5 KV228_HUMAN located_in GO_0005576 \n", + "2 UniProtKB A0A075B6P5 KV228_HUMAN located_in GO_0005576 \n", + "\n", + " 5 6 7 8 9 10 11 12 \\\n", + "0 reactome_R-HSA-166753 TAS None C None None protein taxon:9606 \n", + "1 reactome_R-HSA-166792 TAS None C None None protein taxon:9606 \n", + "2 reactome_R-HSA-173626 TAS None C None None protein taxon:9606 \n", + "\n", + " 13 14 15 16 \n", + "0 20161111 Reactome None None \n", + "1 20161111 Reactome None None \n", + "2 20161111 Reactome None None " + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# download data\n", + "url = 'https://reactome.org/download/current/gene_association.reactome.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'gene_association.reactome'):\n", + " data_downloader(url, unprocessed_data_location, 'gene_association.reactome')\n", + "\n", + "# load data\n", + "rce_go_ptw = pandas.read_csv(unprocessed_data_location + 'gene_association.reactome', header=None, delimiter='\\t', skiprows=4)\n", + "rce_go_ptw.fillna('None', inplace=True)\n", + "rce_go_ptw = rce_go_ptw[rce_go_ptw[12] == 'taxon:9606']\n", + "rce_go_ptw = rce_go_ptw[[x.startswith('REACTOME') for x in rce_go_ptw[5]]]\n", + "\n", + "# fix variable prefixing\n", + "rce_go_ptw[4] = rce_go_ptw[4].str.replace(':', '_')\n", + "rce_go_ptw[5] = rce_go_ptw[5].str.replace('REACTOME:', 'reactome_')\n", + "\n", + "# visualize data\n", + "rce_go_ptw.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Create Metadata Dictionary*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/86795 [00:00<?, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pathway-gocc reactome_R-HSA-166753-GO_0005576 GO_0005576\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "master_metadata_dictionary['edges'].update({'gobp-pathway': {}, 'pathway-gocc': {}, 'pathway-gomf': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(rce_go_ptw.iterrows(), total=rce_go_ptw.shape[0]):\n", + " react = row[5].rstrip(); node_key = row[4]; pathway_db = row[0]; aspect = row[8]\n", + " evidence = [{'Reactome_EvidenceCode': row[6],\n", + " 'Reactome_AssignedBy': row[14],\n", + " 'Reactome_Qualifier': row[3]}]\n", + " # specify edge type, which is related to the ontology aspect\n", + " if aspect == 'P': edge_key = '{}-{}'.format(node_key, react); edge_type = 'gobp-pathway'\n", + " if aspect == 'C': edge_key = '{}-{}'.format(react, node_key); edge_type = 'pathway-gocc'\n", + " if aspect == 'F': edge_key = '{}-{}'.format(react, node_key); edge_type = 'pathway-gomf' \n", + " \n", + " # add reactome metadata\n", + " if react in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][react].keys():\n", + " master_metadata_dictionary['nodes'][react][url]['Reactome_DBReference'] |= {pathway_db}\n", + " else: master_metadata_dictionary['nodes'][react].update({url: {'Reactome_DBReference': {pathway_db}}})\n", + " else: master_metadata_dictionary['nodes'].update({react: {url: {'Reactome_DBReference': {pathway_db}}}})\n", + " \n", + " # add go information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key].keys():\n", + " master_metadata_dictionary['nodes'][node_key][url]['Reactome_Aspect'] |= {aspect}\n", + " else: master_metadata_dictionary['nodes'][node_key].update({url: {'Reactome_Aspect': {aspect}}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key: {url: {'Reactome_Aspect': {aspect}}}})\n", + " \n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'Reactome_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['Reactome_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Reactome_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Reactome_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Reactome_Evidence': evidence}}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `UniProt2Reactome_All_Levels.txt` <a class=\"anchor\" id=\"uniprot-react\"></a>\n", + "\n", + "**Edges:** \n", + "- `protein-pathway` \n", + "\n", + "**Identifier Maps:** \n", + "- Proteins: [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", + "This chunk process the [`UniProt2Reactome_All_Levels.txt`](https://reactome.org/download/current/UniProt2Reactome_All_Levels.txt) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `PathwayName`: A string containing the concept's label. \n", + "- **Edges:** \n", + " - `EvidenceID`: Each annotation includes an evidence code to indicate how the annotation to a particular term is supported\n", + " - Inferred from Experiment (EXP)\n", + " - Inferred from Direct Assay (IDA)\n", + " - Inferred from Physical Interaction (IPI)\n", + " - Inferred from Mutant Phenotype (IMP)\n", + " - Inferred from Genetic Interaction (IGI)\n", + " - Inferred from Expression Pattern (IEP)\n", + " - Inferred from High Throughput Experiment (HTP)\n", + " - Inferred from High Throughput Direct Assay (HDA)\n", + " - Inferred from High Throughput Mutant Phenotype (HMP)\n", + " - Inferred from High Throughput Genetic Interaction (HGI)\n", + " - Inferred from High Throughput Expression Pattern (HEP)\n", + " - Inferred from Biological aspect of Ancestor (IBA)\n", + " - Inferred from Biological aspect of Descendant (IBD)\n", + " - Inferred from Key Residues (IKR)\n", + " - Inferred from Rapid Divergence (IRD)\n", + " - Inferred from Sequence or structural Similarity (ISS)\n", + " - Inferred from Sequence Orthology (ISO)\n", + " - Inferred from Sequence Alignment (ISA)\n", + " - Inferred from Sequence Model (ISM)\n", + " - Inferred from Genomic Context (IGC)\n", + " - Inferred from Reviewed Computational Analysis (RCA)\n", + " - Traceable Author Statement (TAS)\n", + " - Non-traceable Author Statement (NAS)\n", + " - Inferred by Curator (IC)\n", + " - No biological Data available (ND)\n", + " - Inferred from Electronic Annotation (IEA) " + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>0</th>\n", + " <th>1</th>\n", + " <th>2</th>\n", + " <th>3</th>\n", + " <th>4</th>\n", + " <th>5</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>2095</th>\n", + " <td>A0A075B6P5</td>\n", + " <td>reactome_R-HSA-109582</td>\n", + " <td>https://reactome.org/PathwayBrowser/#/R-HSA-10...</td>\n", + " <td>Hemostasis</td>\n", + " <td>TAS</td>\n", + " <td>Homo sapiens</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2096</th>\n", + " <td>A0A075B6P5</td>\n", + " <td>reactome_R-HSA-1280218</td>\n", + " <td>https://reactome.org/PathwayBrowser/#/R-HSA-12...</td>\n", + " <td>Adaptive Immune System</td>\n", + " <td>TAS</td>\n", + " <td>Homo sapiens</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2097</th>\n", + " <td>A0A075B6P5</td>\n", + " <td>reactome_R-HSA-1280218</td>\n", + " <td>https://reactome.org/PathwayBrowser/#/R-HSA-12...</td>\n", + " <td>Adaptive Immune System</td>\n", + " <td>IEA</td>\n", + " <td>Homo sapiens</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " 0 1 \\\n", + "2095 A0A075B6P5 reactome_R-HSA-109582 \n", + "2096 A0A075B6P5 reactome_R-HSA-1280218 \n", + "2097 A0A075B6P5 reactome_R-HSA-1280218 \n", + "\n", + " 2 \\\n", + "2095 https://reactome.org/PathwayBrowser/#/R-HSA-10... \n", + "2096 https://reactome.org/PathwayBrowser/#/R-HSA-12... \n", + "2097 https://reactome.org/PathwayBrowser/#/R-HSA-12... \n", + "\n", + " 3 4 5 \n", + "2095 Hemostasis TAS Homo sapiens \n", + "2096 Adaptive Immune System TAS Homo sapiens \n", + "2097 Adaptive Immune System IEA Homo sapiens " + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# download data\n", - "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/disease_names'\n", - "if not os.path.exists(unprocessed_data_location + 'disease_names'):\n", - " data_downloader(url, unprocessed_data_location)\n", + "url = 'https://reactome.org/download/current/UniProt2Reactome_All_Levels.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'UniProt2Reactome_All_Levels.txt'):\n", + " data_downloader(url, unprocessed_data_location, 'UniProt2Reactome_All_Levels.txt')\n", "\n", "# load data\n", - "disease_names = pandas.read_csv(unprocessed_data_location + 'disease_names',\n", - " header=0, delimiter='\\t', low_memory=False)" + "rce_prot_pth = pandas.read_csv(unprocessed_data_location + 'UniProt2Reactome_All_Levels.txt', header=None, delimiter='\\t', skiprows=0)\n", + "rce_prot_pth = rce_prot_pth[rce_prot_pth[5] == 'Homo sapiens']\n", + "# fix prefixes\n", + "rce_prot_pth[1] = 'reactome_' + rce_prot_pth[1]" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "# replace NaN and \"-\" with 'None'\n", - "disease_names.fillna('None', inplace=True)\n", - "disease_names = disease_names.replace('na', 'None')\n", - "disease_names = disease_names.replace('-', 'None')\n", - "\n", - "# remove rows without a concept id\n", - "disease_names = disease_names[disease_names['ConceptID'] != 'None']\n", - "\n", - "# reformat ConceptId and SourceID\n", - "disease_names['ConceptID'] = disease_names['ConceptID'].apply(lambda x: 'MedGen:' + x.split(':')[0]\n", - " if x.startswith('C') else x.split(':')[-1])\n", - "disease_names['SourceID'] = disease_names['SourceID'].apply(lambda x: 'Orphanet:' + x.split('ORPHA')[1]\n", - " if x.startswith('ORPHA') else x)\n", - "\n", - "# convert date format\n", - "disease_names['LastModified'] = disease_names['LastModified'].str.replace('None', '')\n", - "disease_names['LastModified'] = pandas.to_datetime(disease_names['LastModified'])\n", - "disease_names['LastModified'] = disease_names['LastModified'].dt.strftime('%B %d, %Y')\n", - "disease_names['LastModified'].fillna('None', inplace=True)\n", - "\n", - "# rename variables\n", - "disease_names.rename(columns={'#DiseaseName': 'DiseaseName'}, inplace=True)\n", - "\n", - "# remove unneeded variables\n", - "drop_list = ['DiseaseMIM']\n", - "disease_names = disease_names.drop(drop_list, axis = 1).drop_duplicates()\n", - "\n", - "# print row count and preview data\n", - "print('There are {edge_count} edges'.format(edge_count=len(disease_names)))\n", - "disease_names.head(n=5)" + " *Merge Identifier Maps*" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 30, "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>0</th>\n", + " <th>1</th>\n", + " <th>2</th>\n", + " <th>3</th>\n", + " <th>4</th>\n", + " <th>5</th>\n", + " <th>Uniprot_Accession_IDs</th>\n", + " <th>Protein_Ontology_IDs</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>A0A075B6P5</td>\n", + " <td>reactome_R-HSA-109582</td>\n", + " <td>https://reactome.org/PathwayBrowser/#/R-HSA-10...</td>\n", + " <td>Hemostasis</td>\n", + " <td>TAS</td>\n", + " <td>Homo sapiens</td>\n", + " <td>A0A075B6P5</td>\n", + " <td>PR_A0A075B6P5</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>A0A075B6P5</td>\n", + " <td>reactome_R-HSA-1280218</td>\n", + " <td>https://reactome.org/PathwayBrowser/#/R-HSA-12...</td>\n", + " <td>Adaptive Immune System</td>\n", + " <td>TAS</td>\n", + " <td>Homo sapiens</td>\n", + " <td>A0A075B6P5</td>\n", + " <td>PR_A0A075B6P5</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>A0A075B6P5</td>\n", + " <td>reactome_R-HSA-1280218</td>\n", + " <td>https://reactome.org/PathwayBrowser/#/R-HSA-12...</td>\n", + " <td>Adaptive Immune System</td>\n", + " <td>IEA</td>\n", + " <td>Homo sapiens</td>\n", + " <td>A0A075B6P5</td>\n", + " <td>PR_A0A075B6P5</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " 0 1 \\\n", + "0 A0A075B6P5 reactome_R-HSA-109582 \n", + "1 A0A075B6P5 reactome_R-HSA-1280218 \n", + "2 A0A075B6P5 reactome_R-HSA-1280218 \n", + "\n", + " 2 3 \\\n", + "0 https://reactome.org/PathwayBrowser/#/R-HSA-10... Hemostasis \n", + "1 https://reactome.org/PathwayBrowser/#/R-HSA-12... Adaptive Immune System \n", + "2 https://reactome.org/PathwayBrowser/#/R-HSA-12... Adaptive Immune System \n", + "\n", + " 4 5 Uniprot_Accession_IDs Protein_Ontology_IDs \n", + "0 TAS Homo sapiens A0A075B6P5 PR_A0A075B6P5 \n", + "1 TAS Homo sapiens A0A075B6P5 PR_A0A075B6P5 \n", + "2 IEA Homo sapiens A0A075B6P5 PR_A0A075B6P5 " + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "_Merge and Process Data Sources_" + "rce_prot_pth = rce_prot_pth.merge(uniprot_pro_map, left_on=0, right_on='Uniprot_Accession_IDs')\n", + "\n", + "# visualize data\n", + "rce_prot_pth.head(n=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "*Expand and Enhance Disease and Phenotype Identifiers*\n", - "\n", - "The first step is to try and align as many phenotypes to valid identifiers as possible. To do this, we use the `disease_names` data processed in the prior step to join lower-cased phenotype strings in the `submission_summary.SubmittedPhenotypeInfo` columns (respectively) with strings in the `disease_names.DiseaseName` column." + "*Create Metadata Dictionary*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/131561 [00:00<?, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "protein-pathway PR_A0A075B6P5-reactome_R-HSA-109582 reactome_R-HSA-109582\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ - "# first try to address disease naming issue between the submission_summary and \n", + "master_metadata_dictionary['edges'].update({'protein-pathway': {}})\n", "\n", + "# create dictionary\n", + "for idx, row in tqdm(rce_prot_pth.iterrows(), total=rce_prot_pth.shape[0]):\n", + " pr = row['Protein_Ontology_IDs'].rstrip(); node_key = row[1]; react_name = row[3]\n", + " evidence = [{'Reactome_EvidenceID': row[4]}]\n", + " edge_key = '{}-{}'.format(pr, node_key); edge_type = 'protein-pathway'\n", + " if pr in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[pr]\n", + " else: genomic_info_dict = None\n", + " \n", + " # add reactome information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key].keys():\n", + " master_metadata_dictionary['nodes'][node_key][url]['Reactome_PathwayName'] |= {react_name}\n", + " else:\n", + " master_metadata_dictionary['nodes'][node_key].update({url: {'Reactome_PathwayName': {react_name}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({node_key: {url: {'Reactome_PathwayName': {react_name}}}})\n", + " \n", + " # add genomic information\n", + " if pr in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][pr].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][pr].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({pr: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({pr: {'genomic_data': 'None'}})\n", "\n", - "# merge again, but this time on the provided source and concept identifiers\n" + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'Reactome_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['Reactome_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Reactome_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Reactome_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Reactome_Evidence': evidence}}\n" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, "source": [ - "<br>\n", - "\n", - "#### Metadata Files <a class=\"anchor\" id=\"metadata-files\"></a>\n", "***\n", "\n", - "*Data Files:* \n", - "- [`var_citations.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt) \n", - "- [`allele_gene.txt.gz`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz) \n", - "- [`gene_specific_summary.txt`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt) \n", - "- [`gene_condition_source_id`](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id) \n", - "\n", - "*Processing Details* \n", - "<u>Step 1</u>: The first step is down the `variant_summary.txt.gz`, `submission_summary.txt.gz`, and `disease_names` files. After downloading, the files are cleaned to handle missing data, unneeded variables are removed, identifiers and date fields are cleaned and reformatted, and rows without disease/phenotype identifiers are removed (i.e., [`MedGen:CN517202`](https://www.ncbi.nlm.nih.gov/medgen/CN517202)). \n", + "#### `CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt` <a class=\"anchor\" id=\"variant-disease\"></a>\n", "\n", - "<u>Step 2</u>: Merge the `submission_summary.txt.gz`, and `disease_names` files to try and recover phenotype entries that were initially submitted as a string, but have no identifier." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<br>\n", + "**Edges:** \n", + "- `variant-disease` \n", + "- `variatn-phenotype` \n", "\n", - "[**`var_citations.txt`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt)\n", + "**Identifier Maps:** \n", + "- Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt)\n", + "- Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) \n", "\n", - "> A tab-delimited report of citations associated with data in ClinVar, connected to the AlleleID, the VariationID, and either rs# from dbSNP or nsv in dbVar.\n", - ">\n", - "> - <u>AlleleID</u>: integer value as stored in the AlleleID field in ClinVar \n", - "> - <u>VariationID</u>: The identifier ClinVar uses to anchor its default display \n", - "> - <u>rs</u>: rs identifier from dbSNP, null if missing \n", - "> - <u>nsv</u>: nsv identifier from dbVar, null if missing \n", - "> - <u>citation_source</u>: The source of the citation, either PubMed, PubMedCentral, or the NCBI Bookshelf \n", - "> - <u>citation_id</u>: The identifier used by that source " + "This chunk process the [`CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `Phenotype`: A string containing a disease identifier and prefix. Sources are OMIM, MedGen (UMLS), and Orphanet. \n", + " - `VariantName`: A string containing the name of the variant. \n", + " - `rs_id`: An integer that represents a dbSNP identifier. \n", + " - `AlleleID`: An integer that represents an Allele identifier. \n", + " - `RCVaccession`: An integer that represents an RCV accession identifier. \n", + " - `Type`: Character, the type of variant represented by the AlleleID. \n", + " - `Assembly`: A list of dictionaries, stored as a string, that contains information related to the assembly (i.e., Assembly, ChromosomeAccession, Chromosome, Start, Stop, ReferenceAlel, AlernateAllel, Cytogenetic, and PositionVCF). \n", + "- **Edges:** \n", + " - `OtherIDs`: A \"|\"-delimited list of other identifiers associated with the variant edge. Note that each identifier included also includes a prefix. \n", + " - `Guidelines`: Character, ACMG only right now. \n", + " - `TestedInGTR`: Character, Y/N for Yes/No if there is a test registered as specific to this variant in the NIH Genetic Testing Registry (GTR). \n", + " - `LastEvaluated`: Date, the latest date any submitter reported clinical significance. \n", + " - `ReviewStatus`: Character, highest review status for reporting this measure. \n", + " - `ClinicalSignificance`: Character, comma-separated list of aggregate values of clinical significance calculated for this variant. \n", + " - `ClinSigSimple`: Integer, \n", + " 0 = no current value of Likely pathogenic or Pathogenic; \n", + " 1 = at least one current record submitted with an interpretation of Likely pathogenic or Pathogenic (independent of whether that record includes assertion criteria and evidence). \n", + " -1 = no values for clinical significance at all for this variant or set of variants; used for the \"included\" variants that are only in ClinVar because they are included in a haplotype or genotype with an interpretation. \n", + " - `Origin`: Character, list of all allelic origins for this variant. \n", + " - `OriginSimple`: Character, processed from Origin to make it easier to distinguish between germline and somatic. \n", + " - `SubmitterCategories`: Coded value to indicate whether data were submitted by another resource (1), any other type of source (2), both (3), or none (4). \n", + " - `NumberSubmitters`: Integer, number of submitters describing this variant \n", + " - `Citation`: A \"|\"-delimited list of evidence supporting the variant association. Sources are either PubMed, PubMedCentral, or the NCBI Bookshelf. " ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ "# download data\n", - "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/var_citations.txt'\n", - "if not os.path.exists(unprocessed_data_location + 'var_citations.txt'):\n", - " data_downloader(url, unprocessed_data_location)\n", + "url = 'https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt'):\n", + " data_downloader(url, unprocessed_data_location, 'CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt')\n", "\n", "# load data\n", - "var_citations = pandas.read_csv(unprocessed_data_location + 'var_citations.txt',\n", - " header=0, delimiter='\\t', low_memory=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# replace NaN with 'None'\n", - "var_citations.fillna('None', inplace=True)\n", - "var_citations = var_citations.replace('na', 'None')\n", - "\n", - "# replace cells that contain \"-\" with 'None'\n", - "var_citations = var_citations.replace('-', 'None')\n", - "\n", - "# handle rs ids that may be coded as -1\n", - "var_citations = var_citations[var_citations['rs'] != -1]\n", - "\n", - "# convert rs id to integer\n", - "var_citations['rs'] = var_citations['rs'].str.replace('None', '00000')\n", - "var_citations['rs'] = var_citations['rs'].astype(int)\n", - "var_citations['rs'] = var_citations['rs'].str.replace(0000, 'None')\n", - "\n", - "# rename variables\n", - "var_citations.rename(columns={'#AlleleID': 'AlleleID',\n", - " 'rs': 'RS# (dbSNP)'}, inplace=True)\n", - "\n", - "# remove unneeded variables\n", - "drop_list = ['nsv']\n", - "var_citations = var_citations.drop(drop_list, axis = 1).drop_duplicates()\n", - "\n", - "# print row count and preview data\n", - "print('There are {edge_count} edges'.format(edge_count=len(var_citations)))\n", - "var_citations.head(n=5)" + "clv_var_dis = pandas.read_csv(unprocessed_data_location + 'CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt', header=0, delimiter='\\t', low_memory=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "<br>\n", - "\n", - "[**`allele_gene.txt.gz`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz)\n", - "\n", - "> Reports per ClinVar's AlleleID, the genes that are related to that gene and how they are related.\n", - ">\n", - "> - <u>AlleleID</u>: the integer identifier assigned by ClinVar to each simple allele\n", - "> - <u>GeneID</u>: integer, GeneID in NCBI's Gene database \n", - "> - <u>Symbol</u>: character, Symbol preferred in NCBI's Gene database. Is the symbol from HGNC when available \n", - "> - <u>Name</u>: character, full name of the gene \n", - "> - <u>GenesPerAlleleID</u>: integer, number of genes related to the allele \n", - "> - <u>Category</u>: character, type of allele-gene relationship. The values for category are:\n", - "> - <u>asserted, but not computed</u>: Submitted as related to a gene, but not within the location of that gene on the genome \n", - "> - <u>genes overlapped by variant</u>: The gene and variant overlap \n", - "> - <u>near gene, downstream</u>: Outside the location of the gene on the genome, within 5 kb \n", - "> - <u>near gene, upstream</u>: Outside the location of the gene on the genome, within 5 kb \n", - "> - <u>within multiple genes by overlap</u>: The variant is within genes that overlap on the genome. Includes introns \n", - "> - <u>within single gene</u>: The variant is in only one gene. Includes introns \n", - "> - <u>Source</u>: character, was the relationship submitted or calculated? " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# download data\n", - "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/allele_gene.txt.gz'\n", - "if not os.path.exists(unprocessed_data_location + 'allele_gene.txt'):\n", - " data_downloader(url, unprocessed_data_location)\n", - "\n", - "# load data\n", - "allele_gene = pandas.read_csv(unprocessed_data_location + 'allele_gene.txt',\n", - " header=0, delimiter='\\t', low_memory=False)" + " *Merge Identifier Maps*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 74, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>VariationID</th>\n", + " <th>AlleleID</th>\n", + " <th>RS# (dbSNP)</th>\n", + " <th>Type</th>\n", + " <th>VariantName</th>\n", + " <th>RCVaccession</th>\n", + " <th>LastEvaluated</th>\n", + " <th>ReviewStatus</th>\n", + " <th>ClinicalSignificance</th>\n", + " <th>ClinSigSimple</th>\n", + " <th>...</th>\n", + " <th>Origin</th>\n", + " <th>OriginSimple</th>\n", + " <th>Assembly</th>\n", + " <th>Phenotype</th>\n", + " <th>Citation</th>\n", + " <th>OtherIDs</th>\n", + " <th>Disease_IDs_x</th>\n", + " <th>MONDO_IDs</th>\n", + " <th>Disease_IDs_y</th>\n", + " <th>HP_IDs</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>clinvar_9</td>\n", + " <td>15048</td>\n", + " <td>1800562.0</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_000410.4(HFE):c.845G&gt;A (p.Cys282Tyr)</td>\n", + " <td>RCV000000023|RCV000000025|RCV000000019|RCV0000...</td>\n", + " <td>September 15, 2021</td>\n", + " <td>criteria provided, conflicting interpretations</td>\n", + " <td>Conflicting interpretations of pathogenicity, ...</td>\n", + " <td>1</td>\n", + " <td>...</td>\n", + " <td>germline;unknown</td>\n", + " <td>germline</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0004975</td>\n", + " <td>NCBIBookShelf:NBK1440|PubMed:10401000|PubMed:1...</td>\n", + " <td>UniProtKB:Q30201#VAR_004398|OMIM:613609.0001|C...</td>\n", + " <td>MONDO:0004975</td>\n", + " <td>MONDO_0004975</td>\n", + " <td>MONDO:0004975</td>\n", + " <td>HP_0002511</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>clinvar_8847</td>\n", + " <td>23886</td>\n", + " <td>63750110.0</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_000447.3(PSEN2):c.1316A&gt;C (p.Asp439Ala)</td>\n", + " <td>RCV000009395|RCV000084269|RCV000172102</td>\n", + " <td>June 24, 2013</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>Uncertain significance</td>\n", + " <td>1</td>\n", + " <td>...</td>\n", + " <td>germline;unknown</td>\n", + " <td>germline</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0004975</td>\n", + " <td>PubMed:23861362|PubMed:11723295</td>\n", + " <td>ClinGen:CA224963|OMIM:600759.0003</td>\n", + " <td>MONDO:0004975</td>\n", + " <td>MONDO_0004975</td>\n", + " <td>MONDO:0004975</td>\n", + " <td>HP_0002511</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>clinvar_8852</td>\n", + " <td>23891</td>\n", + " <td>63750197.0</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_000447.3(PSEN2):c.389C&gt;T (p.Ser130Leu)</td>\n", + " <td>RCV000009401|RCV000009400|RCV000084261|RCV0001...</td>\n", + " <td>December 30, 2020</td>\n", + " <td>criteria provided, multiple submitters, no con...</td>\n", + " <td>Benign/Likely benign</td>\n", + " <td>1</td>\n", + " <td>...</td>\n", + " <td>germline;unknown</td>\n", + " <td>germline</td>\n", + " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", + " <td>MONDO:0004975</td>\n", + " <td>PubMed:28492532|PubMed:30045758|PubMed:1462372...</td>\n", + " <td>ClinGen:CA224951|UniProtKB:P49810#VAR_064903|O...</td>\n", + " <td>MONDO:0004975</td>\n", + " <td>MONDO_0004975</td>\n", + " <td>MONDO:0004975</td>\n", + " <td>HP_0002511</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>3 rows × 24 columns</p>\n", + "</div>" + ], + "text/plain": [ + " VariationID AlleleID RS# (dbSNP) Type \\\n", + "0 clinvar_9 15048 1800562.0 single nucleotide variant \n", + "1 clinvar_8847 23886 63750110.0 single nucleotide variant \n", + "2 clinvar_8852 23891 63750197.0 single nucleotide variant \n", + "\n", + " VariantName \\\n", + "0 NM_000410.4(HFE):c.845G>A (p.Cys282Tyr) \n", + "1 NM_000447.3(PSEN2):c.1316A>C (p.Asp439Ala) \n", + "2 NM_000447.3(PSEN2):c.389C>T (p.Ser130Leu) \n", + "\n", + " RCVaccession LastEvaluated \\\n", + "0 RCV000000023|RCV000000025|RCV000000019|RCV0000... September 15, 2021 \n", + "1 RCV000009395|RCV000084269|RCV000172102 June 24, 2013 \n", + "2 RCV000009401|RCV000009400|RCV000084261|RCV0001... December 30, 2020 \n", + "\n", + " ReviewStatus \\\n", + "0 criteria provided, conflicting interpretations \n", + "1 criteria provided, single submitter \n", + "2 criteria provided, multiple submitters, no con... \n", + "\n", + " ClinicalSignificance ClinSigSimple ... \\\n", + "0 Conflicting interpretations of pathogenicity, ... 1 ... \n", + "1 Uncertain significance 1 ... \n", + "2 Benign/Likely benign 1 ... \n", + "\n", + " Origin OriginSimple \\\n", + "0 germline;unknown germline \n", + "1 germline;unknown germline \n", + "2 germline;unknown germline \n", + "\n", + " Assembly Phenotype \\\n", + "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0004975 \n", + "1 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0004975 \n", + "2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0004975 \n", + "\n", + " Citation \\\n", + "0 NCBIBookShelf:NBK1440|PubMed:10401000|PubMed:1... \n", + "1 PubMed:23861362|PubMed:11723295 \n", + "2 PubMed:28492532|PubMed:30045758|PubMed:1462372... \n", + "\n", + " OtherIDs Disease_IDs_x \\\n", + "0 UniProtKB:Q30201#VAR_004398|OMIM:613609.0001|C... MONDO:0004975 \n", + "1 ClinGen:CA224963|OMIM:600759.0003 MONDO:0004975 \n", + "2 ClinGen:CA224951|UniProtKB:P49810#VAR_064903|O... MONDO:0004975 \n", + "\n", + " MONDO_IDs Disease_IDs_y HP_IDs \n", + "0 MONDO_0004975 MONDO:0004975 HP_0002511 \n", + "1 MONDO_0004975 MONDO:0004975 HP_0002511 \n", + "2 MONDO_0004975 MONDO:0004975 HP_0002511 \n", + "\n", + "[3 rows x 24 columns]" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# replace NaN with 'None'\n", - "allele_gene.fillna('None', inplace=True)\n", - "allele_gene = allele_gene.replace('na', 'None')\n", + "clv_var_dis = clv_var_dis.merge(disease_maps, left_on='Phenotype', right_on='Disease_IDs')\n", + "clv_var_dis = clv_var_dis.merge(phenotype_maps, left_on='Phenotype', right_on='Disease_IDs')\n", "\n", - "# replace cells that contain \"-\" with 'None'\n", - "allele_gene = allele_gene.replace('-', 'None')\n", - "\n", - "# handle gene ids that may be coded as -1\n", - "allele_gene = allele_gene[allele_gene['GeneID'] != -1]\n", - "\n", - "# rename variables\n", - "allele_gene.rename(columns={'#AlleleID': 'AlleleID'}, inplace=True)\n", - "\n", - "# print row count and preview data\n", - "print('There are {edge_count} edges'.format(edge_count=len(allele_gene)))\n", - "allele_gene.head(n=5)" + "# visualize data\n", + "clv_var_dis.head(n=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "<br>\n", - "\n", - "[**`gene_specific_summary.txt`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt)\n", - "\n", - "> A tab-delimited report, for each gene, of the number of submissions and the number of different variants (alleles).\n", - "> Because some variant-gene relationships are submitted, and some are calculated from overlapping annotation, in January of 2015, the report was modified to indicate when the gene-variant relationship was submitted.\n", - "> \n", - "> - <u>Symbol</u>: Gene symbol (if officially named, from HGNC, else from NCBI's Gene database) \n", - "> - <u>GeneID</u>: Unique identifier from NCBI's Gene database \n", - "> - <u>Total_submissions</u>: Total submissions to ClinVar with variants in/overlapping this gene \n", - "> - <u>Total_alleles</u>: Number of alleles submitted to ClinVar for this gene \n", - "> - <u>Submissions_reporting_this_gene</u>: Subset of the total submissions that also reported the gene \n", - "> - <u>Alleles_reported_Pathogenic_Likely_pathogenic</u>: Number of variants reported as pathogenic or likely pathogenic. Excludes structural variants that may overlap a gene \n", - "> - <u>Gene_MIM_Number</u>: The MIM number for this gene \n", - "> - <u>Number_Uncertain</u>: Submissions with an interpretation of 'Uncertain significance' \n", - "> - <u>Number_with_conflicts</u>: Number of VariationIDs for this gene with conflicting interpretations " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# download data\n", - "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/gene_specific_summary.txt'\n", - "if not os.path.exists(unprocessed_data_location + 'gene_specific_summary.txt'):\n", - " data_downloader(url, unprocessed_data_location)\n", - "\n", - "# load data\n", - "gene_summary = pandas.read_csv(unprocessed_data_location + 'gene_specific_summary.txt',\n", - " header=0, skiprows=1, delimiter='\\t', low_memory=False)" + "*Create Metadata Dictionary*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 79, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/17500 [00:00<?, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "variant-phenotype clinvar_9-HP_0002511 clinvar_9\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ - "# replace NaN with 'None'\n", - "gene_summary.fillna('None', inplace=True)\n", - "gene_summary = gene_summary.replace('na', 'None')\n", - "\n", - "# replace cells that contain \"-\" with 'None'\n", - "gene_summary = gene_summary.replace('-', 'None')\n", - "\n", - "# handle gene ids that may be coded as -1\n", - "gene_summary = gene_summary[gene_summary['GeneID'] != -1]\n", - "\n", - "# rename variables\n", - "gene_summary.rename(columns={'#Symbol': 'Symbol'}, inplace=True)\n", - "\n", - "# remove unneeded variables\n", - "drop_list = ['Gene_MIM_number']\n", - "gene_summary = gene_summary.drop(drop_list, axis = 1).drop_duplicates()\n", - "\n", - "# print row count and preview data\n", - "print('There are {edge_count} edges'.format(edge_count=len(gene_summary)))\n", - "gene_summary.head(n=5)" + "master_metadata_dictionary['edges'].update({'variant-disease': {}, 'variant-phenotype': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(clv_var_dis.iterrows(), total=clv_var_dis.shape[0]):\n", + " node_key = row['VariationID']; rcv = row['RCVaccession']; var_type = row['Type']\n", + " pheno = row['Phenotype']; rs_id = row['RS# (dbSNP)']; allele_id = row['AlleleID']\n", + " assembly = row['Assembly']; var_name = row['VariantName']\n", + " evidence = [{'ClinVar_OtherIDs': row['OtherIDs'],\n", + " 'ClinVar_Guidelines': row['Guidelines'],\n", + " 'ClinVar_TestedInGTR': row['TestedInGTR'],\n", + " 'ClinVar_LastEvaluated': row['LastEvaluated'],\n", + " 'ClinVar_ReviewStatus': row['ReviewStatus'],\n", + " 'ClinVar_ClinicalSignificance': row['ClinicalSignificance'],\n", + " 'ClinVar_ClinSigSimple': row['ClinSigSimple'],\n", + " 'ClinVar_Origin': row['Origin'],\n", + " 'ClinVar_OriginSimple': row['OriginSimple'],\n", + " 'ClinVar_SubmitterCategories': row['SubmitterCategories'],\n", + " 'ClinVar_NumberSubmitters': row['NumberSubmitters'],\n", + " 'ClinVar_Citation': row['Citation']}] \n", + " for idx in [row['MONDO_IDs'].rstrip(), row['HP_IDs'].rstrip()]:\n", + " if idx.startswith('MONDO'): edge_key = '{}-{}'.format(node_key, idx); edge_type = 'variant-disease'\n", + " else: edge_key = '{}-{}'.format(node_key, idx); edge_type = 'variant-phenotype'\n", + " \n", + " # add disease/phenotype metadata\n", + " if idx in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][idx].keys():\n", + " master_metadata_dictionary['nodes'][idx][url]['ClinVar_Phenotype'] |= {pheno}\n", + " else: master_metadata_dictionary['nodes'][idx].update({url: {'ClinVar_Phenotype': {pheno}}})\n", + " else: master_metadata_dictionary['nodes'].update({idx: {url: {'ClinVar_Phenotype': {pheno}}}})\n", + "\n", + " # add variant information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key].keys():\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_VariantName'] |= {var_name}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_rs_id'] |= {rs_id}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_AlleleID'] |= {allele_id}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_RCVaccession'] |= {rcv}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_Type'] |= {var_type}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_Assembly'] |= {assembly}\n", + " else:\n", + " master_metadata_dictionary['nodes'][node_key].update({\n", + " url: {'ClinVar_rs_id': {var_name},\n", + " 'ClinVar_VariantName': {rs_id},\n", + " 'ClinVar_AlleleID': {allele_id},\n", + " 'ClinVar_RCVaccession': {rcv},\n", + " 'ClinVar_Type': {var_type},\n", + " 'ClinVar_Assembly': {assembly}\n", + " }})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({node_key: {\n", + " url: {'ClinVar_rs_id': {var_name},\n", + " 'ClinVar_rs_id': {rs_id},\n", + " 'ClinVar_AlleleID': {allele_id},\n", + " 'ClinVar_RCVaccession': {rcv},\n", + " 'ClinVar_Type': {var_type},\n", + " 'ClinVar_Assembly': {assembly}}}})\n", + "\n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'ClinVar_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['ClinVar_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['ClinVar_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'ClinVar_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'ClinVar_Evidence': evidence}}\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "<br>\n", - "\n", - "[**`gene_condition_source_id`**](https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id)\n", + "***\n", "\n", - "> Tab-delimited file with the following fields:\n", - "> \n", - "> - <u>GeneID</u>: The NCBI GeneID \n", - "> - <u>AssociatedGenes</u>: The preferred symbol corresponding to the GeneID for the gene reported to be causative for this disorder \n", - "> - <u>RelatedGenes</u>: The preferred symbol corresponding to any gene that may contribute to a disorder. This column is null for monogenic disorders, but will be reported for broader concepts. For example, ABCA4 is reported as an AssociatedGene for Retinitis pigmentosa 19, but a related gene for Retinitis pigmentosa \n", - "> - <u>ConceptID</u>: The identifier assigned to a disorder associated with this gene. If the value starts with a C and is followed by digits, the ConceptID is a value from UMLS; if a value begins with CN, it was created by NCBI-based processing \n", - "> - <u>DiseaseName</u>: Full name for the condition \n", - "> - <u>SourceName</u>: Sources that use this name \n", - "> - <u>SourceID</u>: The identifier used by this source \n", - "> - <u>DiseaseMIM</u>: MIM number for the condition \n", - "> - <u>LastUpdated</u>: Last time this record was modified by NCBI staff " - ] - }, - { - "cell_type": "code", - "execution_count": null, + "#### `CLINVAR_VARIANT_GENE_EDGES.txt` <a class=\"anchor\" id=\"variant-gene\"></a>\n", + "\n", + "**Edges:** \n", + "- `variant-gene` \n", + "\n", + "This chunk process the [`CLINVAR_VARIANT_GENE_EDGES.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_EDGES.txt) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `VariantName`: A string containing the name of the variant. \n", + " - `rs_id`: An integer that represents a dbSNP identifier. \n", + " - `AlleleID`: An integer that represents an Allele identifier. \n", + " - `RCVaccession`: An integer that represents an RCV accession identifier. \n", + " - `Type`: Character, the type of variant represented by the AlleleID. \n", + " - `Assembly`: A list of dictionaries, stored as a string, that contains information related to the assembly (i.e., Assembly, ChromosomeAccession, Chromosome, Start, Stop, ReferenceAlel, AlernateAllel, Cytogenetic, and PositionVCF). \n", + " - `GenesPerAlleleID`: An integer that represents the count of genes that are found in the allele which corresponds to the variant. \n", + " - `Category`: The type of allele-gene relationship. The values for category are:\n", + " - Asserted, but not computed: Submitted as related to a gene, but not within the location of that gene on the genome\n", + " - Genes overlapped by variant: The gene and variant overlap\n", + " - Near gene, downstream: Outside the location of the gene on the genome, within 5 kb\n", + " -Near gene, upstream: Outside the location of the gene on the genome, within 5 kb\n", + " - Within multiple genes by overlap: The variant is within genes that overlap on the genome. Includes introns\n", + " - Within single gene: The variant is in only one gene. Includes introns\n", + "- **Edges:** \n", + " - `OtherIDs`: A \"|\"-delimited list of other identifiers associated with the variant edge. Note that each identifier included also includes a prefix. \n", + " - `Guidelines`: Character, ACMG only right now. \n", + " - `TestedInGTR`: Character, Y/N for Yes/No if there is a test registered as specific to this variant in the NIH Genetic Testing Registry (GTR). \n", + " - `LastEvaluated`: Date, the latest date any submitter reported clinical significance. \n", + " - `ReviewStatus`: Character, highest review status for reporting this measure. \n", + " - `ClinicalSignificance`: Character, comma-separated list of aggregate values of clinical significance calculated for this variant. \n", + " - `ClinSigSimple`: Integer, \n", + " 0 = no current value of Likely pathogenic or Pathogenic; \n", + " 1 = at least one current record submitted with an interpretation of Likely pathogenic or Pathogenic (independent of whether that record includes assertion criteria and evidence). \n", + " -1 = no values for clinical significance at all for this variant or set of variants; used for the \"included\" variants that are only in ClinVar because they are included in a haplotype or genotype with an interpretation. \n", + " - `Origin`: Character, list of all allelic origins for this variant. \n", + " - `OriginSimple`: Character, processed from Origin to make it easier to distinguish between germline and somatic. \n", + " - `SubmitterCategories`: Coded value to indicate whether data were submitted by another resource (1), any other type of source (2), both (3), or none (4). \n", + " - `NumberSubmitters`: Integer, number of submitters describing this variant \n", + " - `Citation`: A \"|\"-delimited list of evidence supporting the variant association. Sources are either PubMed, PubMedCentral, or the NCBI Bookshelf. " + ] + }, + { + "cell_type": "code", + "execution_count": 84, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>VariationID</th>\n", + " <th>AlleleID</th>\n", + " <th>RS# (dbSNP)</th>\n", + " <th>Type</th>\n", + " <th>VariantName</th>\n", + " <th>OtherIDs</th>\n", + " <th>GeneID</th>\n", + " <th>GeneSymbol</th>\n", + " <th>GeneName</th>\n", + " <th>GenesPerAlleleID</th>\n", + " <th>...</th>\n", + " <th>LastEvaluated</th>\n", + " <th>ReviewStatus</th>\n", + " <th>ClinicalSignificance</th>\n", + " <th>ClinSigSimple</th>\n", + " <th>Origin</th>\n", + " <th>OriginSimple</th>\n", + " <th>Source</th>\n", + " <th>SubmitterCategories</th>\n", + " <th>NumberSubmitters</th>\n", + " <th>Citation</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>clinvar_2</td>\n", + " <td>15041</td>\n", + " <td>397704705.0</td>\n", + " <td>Indel</td>\n", + " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", + " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", + " <td>NCBIGene_9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", + " <td>1.0</td>\n", + " <td>...</td>\n", + " <td>NaN</td>\n", + " <td>criteria provided, single submitter</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>germline;unknown</td>\n", + " <td>germline</td>\n", + " <td>submitted</td>\n", + " <td>3</td>\n", + " <td>2</td>\n", + " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>clinvar_3</td>\n", + " <td>15042</td>\n", + " <td>397704709.0</td>\n", + " <td>Deletion</td>\n", + " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", + " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", + " <td>NCBIGene_9907</td>\n", + " <td>AP5Z1</td>\n", + " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", + " <td>1.0</td>\n", + " <td>...</td>\n", + " <td>June 29, 2010</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>Pathogenic</td>\n", + " <td>1</td>\n", + " <td>germline</td>\n", + " <td>germline</td>\n", + " <td>submitted</td>\n", + " <td>1</td>\n", + " <td>1</td>\n", + " <td>PubMed:20613862</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>clinvar_4</td>\n", + " <td>15043</td>\n", + " <td>150829393.0</td>\n", + " <td>single nucleotide variant</td>\n", + " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", + " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", + " <td>NCBIGene_9640</td>\n", + " <td>ZNF592</td>\n", + " <td>zinc finger protein 592</td>\n", + " <td>1.0</td>\n", + " <td>...</td>\n", + " <td>June 29, 2015</td>\n", + " <td>no assertion criteria provided</td>\n", + " <td>Uncertain significance</td>\n", + " <td>0</td>\n", + " <td>germline</td>\n", + " <td>germline</td>\n", + " <td>submitted</td>\n", + " <td>1</td>\n", + " <td>1</td>\n", + " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>3 rows × 25 columns</p>\n", + "</div>" + ], + "text/plain": [ + " VariationID AlleleID RS# (dbSNP) Type \\\n", + "0 clinvar_2 15041 397704705.0 Indel \n", + "1 clinvar_3 15042 397704709.0 Deletion \n", + "2 clinvar_4 15043 150829393.0 single nucleotide variant \n", + "\n", + " VariantName \\\n", + "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... \n", + "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) \n", + "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) \n", + "\n", + " OtherIDs GeneID \\\n", + "0 ClinGen:CA215070|OMIM:613653.0001 NCBIGene_9907 \n", + "1 ClinGen:CA215072|OMIM:613653.0002 NCBIGene_9907 \n", + "2 ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... NCBIGene_9640 \n", + "\n", + " GeneSymbol GeneName \\\n", + "0 AP5Z1 adaptor related protein complex 5 subunit zeta 1 \n", + "1 AP5Z1 adaptor related protein complex 5 subunit zeta 1 \n", + "2 ZNF592 zinc finger protein 592 \n", + "\n", + " GenesPerAlleleID ... LastEvaluated ReviewStatus \\\n", + "0 1.0 ... NaN criteria provided, single submitter \n", + "1 1.0 ... June 29, 2010 no assertion criteria provided \n", + "2 1.0 ... June 29, 2015 no assertion criteria provided \n", + "\n", + " ClinicalSignificance ClinSigSimple Origin OriginSimple \\\n", + "0 Pathogenic 1 germline;unknown germline \n", + "1 Pathogenic 1 germline germline \n", + "2 Uncertain significance 0 germline germline \n", + "\n", + " Source SubmitterCategories NumberSubmitters \\\n", + "0 submitted 3 2 \n", + "1 submitted 1 1 \n", + "2 submitted 1 1 \n", + "\n", + " Citation \n", + "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", + "1 PubMed:20613862 \n", + "2 PubMed:26123727|PubMed:12030328|PubMed:20531441 \n", + "\n", + "[3 rows x 25 columns]" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# download data\n", - "url = 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/gene_condition_source_id'\n", - "if not os.path.exists(unprocessed_data_location + 'gene_condition_source_id'):\n", - " data_downloader(url, unprocessed_data_location)\n", + "url = 'https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_EDGES.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'CLINVAR_VARIANT_GENE_EDGES.txt'):\n", + " data_downloader(url, unprocessed_data_location, 'CLINVAR_VARIANT_GENE_EDGES.txt')\n", "\n", "# load data\n", - "gene_cond_src = pandas.read_csv(unprocessed_data_location + 'gene_condition_source_id',\n", - " header=0, delimiter='\\t', low_memory=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# replace NaN with 'None'\n", - "gene_cond_src.fillna('None', inplace=True)\n", - "gene_cond_src = gene_cond_src.replace('na', 'None')\n", - "\n", - "# replace cells that contain \"-\" with 'None'\n", - "gene_cond_src = gene_cond_src.replace('-', 'None')\n", + "clv_var_gene = pandas.read_csv(unprocessed_data_location + 'CLINVAR_VARIANT_GENE_EDGES.txt', header=0, delimiter='\\t', low_memory=False)\n", "\n", - "# handle gene ids that may be coded as -1\n", - "gene_cond_src = gene_cond_src[gene_cond_src['#GeneID'] != -1]\n", - "\n", - "# handle gene ids that may be coded as -1\n", - "gene_cond_src = gene_cond_src[gene_cond_src['ConceptID'] != -1]\n", - "gene_cond_src = gene_cond_src[gene_cond_src['ConceptID'] != 'None']\n", - "\n", - "# reformat ReportedPhenotypeInfo to match formatting in variant summary\n", - "gene_cond_src['ConceptID'] = gene_cond_src['ConceptID'].apply(lambda x: 'MedGen:' + x.split(':')[0]\n", - " if x.startswith('C') else x.split(':')[-1])\n", - "\n", - "# convert date format\n", - "gene_cond_src['LastUpdated'] = gene_cond_src['LastUpdated'].str.replace('None', '')\n", - "gene_cond_src['LastUpdated'] = pandas.to_datetime(gene_cond_src['LastUpdated'])\n", - "gene_cond_src['LastUpdated'] = gene_cond_src['LastUpdated'].dt.strftime('%B %d, %Y')\n", - "gene_cond_src['LastUpdated'].fillna('None', inplace=True)\n", - "\n", - "# rename variables\n", - "gene_cond_src.rename(columns={'#GeneID': 'GeneID'}, inplace=True)\n", - "\n", - "# remove unneeded variables\n", - "drop_list = ['DiseaseMIM']\n", - "gene_cond_src = gene_cond_src.drop(drop_list, axis = 1).drop_duplicates()\n", - "\n", - "# print row count and preview data\n", - "print('There are {edge_count} edges'.format(edge_count=len(gene_cond_src)))\n", - "gene_cond_src.head(n=5)" + "# visualize data\n", + "clv_var_gene.head(n=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "_Merge and Process Data Sources_" + "*Create Metadata Dictionary*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 91, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/1161070 [00:00<?, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "variant-gene clinvar_2-NCBIGene_9907 clinvar_2\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ - "# first try to address disease naming issue between the submission_summary and \n" + "master_metadata_dictionary['edges'].update({'variant-gene': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(clv_var_gene.iterrows(), total=clv_var_gene.shape[0]):\n", + " node_key = row['VariationID']; rcv = row['RCVaccession']; var_type = row['Type']\n", + " gene = row['GeneID']; var_name = row['VariantName']\n", + " rs_id = row['RS# (dbSNP)']; allele_id = row['AlleleID']\n", + " assembly = row['Assembly']; gpa = row['GenesPerAlleleID']; category = row['Category']\n", + " evidence = [{'ClinVar_OtherIDs': row['OtherIDs'],\n", + " 'ClinVar_Guidelines': row['Guidelines'],\n", + " 'ClinVar_TestedInGTR': row['TestedInGTR'],\n", + " 'ClinVar_LastEvaluated': row['LastEvaluated'],\n", + " 'ClinVar_ReviewStatus': row['ReviewStatus'],\n", + " 'ClinVar_ClinicalSignificance': row['ClinicalSignificance'],\n", + " 'ClinVar_ClinSigSimple': row['ClinSigSimple'],\n", + " 'ClinVar_Origin': row['Origin'],\n", + " 'ClinVar_OriginSimple': row['OriginSimple'],\n", + " 'ClinVar_SubmitterCategories': row['SubmitterCategories'],\n", + " 'ClinVar_NumberSubmitters': row['NumberSubmitters'],\n", + " 'ClinVar_Citation': row['Citation']}] \n", + " edge_key = '{}-{}'.format(node_key, gene); edge_type = 'variant-gene'\n", + " \n", + " # add disease/phenotype metadata\n", + " if idx in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][idx].keys():\n", + " master_metadata_dictionary['nodes'][idx][url]['ClinVar_Phenotype'] |= {pheno}\n", + " else: master_metadata_dictionary['nodes'][idx].update({url: {'ClinVar_Phenotype': {pheno}}})\n", + " else: master_metadata_dictionary['nodes'].update({idx: {url: {'ClinVar_Phenotype': {pheno}}}})\n", + "\n", + " # add genomic information\n", + " if gene in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[gene]\n", + " else: genomic_info_dict = None\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][gene].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][gene].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({gene: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({gene: {'genomic_data': 'None'}})\n", + " \n", + " # add variant information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key].keys():\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_VariantName'] |= {var_name}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_rs_id'] |= {rs_id}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_AlleleID'] |= {allele_id}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_RCVaccession'] |= {rcv}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_Type'] |= {var_type}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_Assembly'] |= {assembly}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_GenesPerAlleleID'] |= {gpa}\n", + " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_Category'] |= {category}\n", + " else:\n", + " master_metadata_dictionary['nodes'][node_key].update({\n", + " url: {'ClinVar_rs_id': {var_name},\n", + " 'ClinVar_VariantName': {rs_id},\n", + " 'ClinVar_AlleleID': {allele_id},\n", + " 'ClinVar_RCVaccession': {rcv},\n", + " 'ClinVar_Type': {var_type},\n", + " 'ClinVar_Assembly': {assembly},\n", + " 'ClinVar_GenesPerAlleleID': {gpa},\n", + " 'ClinVar_Category': {category}\n", + " }})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({node_key: {\n", + " url: {'ClinVar_rs_id': {var_name},\n", + " 'ClinVar_rs_id': {rs_id},\n", + " 'ClinVar_AlleleID': {allele_id},\n", + " 'ClinVar_RCVaccession': {rcv},\n", + " 'ClinVar_Type': {var_type},\n", + " 'ClinVar_Assembly': {assembly},\n", + " 'ClinVar_GenesPerAlleleID': {gpa},\n", + " 'ClinVar_Category': {category}}}})\n", + "\n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'ClinVar_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['ClinVar_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['ClinVar_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'ClinVar_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'ClinVar_Evidence': evidence}}\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "\n", - "<br>\n", - "\n", "***\n", "\n", - "### Uniprot Protein-Cofactor and Protein-Catalyst <a class=\"anchor\" id=\"uniprot-protein-cofactorcatalyst\"></a>\n", - "\n", - "**Data Source Wiki Page:** [Uniprot](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#uniprot-knowledgebase) \n", - "\n", - "**Purpose:** This script downloads the [uniprot-cofactor-catalyst.tab](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#uniprot-knowledgebase) file from the [Uniprot Knowledge Base](https://www.uniprot.org) in order to create the following edges: \n", - "- protein-cofactor \n", - "- protein-catalyst \n", - "\n", - "**Data:** This data was obtained by querying the [UniProt Knowledgebase](https://www.uniprot.org/uniprot/) using the *reviewed:yes AND organism:\"Homo sapiens (Human) [9606]\"\"* keyword and including the following columns:\n", - "- Entry (Standard) \n", - "- Status (Standard) \n", - "- PRO (*Miscellaneous*) \n", - "- ChEBI (Cofactor) (*Chemical entities*) \n", - "- ChEBI (Catalytic activity) (*Chemical entities*) \n", - "\n", - "The URL to access the results of this query is obtained by clicking on the share symbol and copying the free-text from the box. To obtain the data in a tab-delimited format the following string is appended to the end of the URL: \"&format=tab\".\n", + "#### `HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt` <a class=\"anchor\" id=\"hpa\"></a>\n", "\n", - "**NOTE.** Be sure to obtain a new URL from the [UniProt Knowledgebase](https://www.uniprot.org/uniprot/) when rebuilding to ensure you are getting the most up-to-date data. This query was last generated on `12/02/2020`.\n", + "**Edges:** \n", + "- `protein-anatomy` \n", + "- `protein-cell` \n", + "- `rna-anatomy` \n", + "- `rna-cell` \n", "\n", - "<br>\n", + "**Identifier Maps:** \n", + "- Proteins: [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", + "- Anatomy: [HPA_GTEx_TISSUE_CELL_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt)\n", + "- Cells: [HPA_GTEx_TISSUE_CELL_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt) \n", + "- RNA: [GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt) \n", "\n", - "**Output:** \n", - "- protein-cofactor ➞ `UNIPROT_PROTEIN_COFACTOR.txt`\n", - "- protein-catalyst ➞ `UNIPROT_PROTEIN_CATALYST.txt`\n" + "This chunk process the [`HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `Anatomy`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. \n", + " - `Anatomy_Type`: A string indicating the type of annotation. \n", + " - `Subcellular_Location`: A string containing a subcellular compartment. \n", + "- **Edges:** \n", + " - `Expression_Value`: The expression value derived from the experiments. \n", + " - `Source`: A string indicating the source of the data (i.e., Human Protein Atlas or the Genotype-Tissue Expression project). \n", + " - `Evidence`: A string indicating if the evidence is at the transcript or protein level. " ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 108, "metadata": {}, "outputs": [], "source": [ "# download data\n", - "url = 'https://www.uniprot.org/uniprot/?query=&fil=organism%3A%22Homo%20sapiens%20(Human)%20%5B9606%5D%22&columns=id%2Creviewed%2Centry%20name%2Cdatabase(PRO)%2Cchebi(Cofactor)%2Cchebi(Catalytic%20activity)&format=tab'\n", - "if not os.path.exists(unprocessed_data_location + 'uniprot-cofactor-catalyst.tab'):\n", - " data_downloader(url, unprocessed_data_location, 'uniprot-cofactor-catalyst.tab')\n", + "url = 'https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt'):\n", + " data_downloader(url, unprocessed_data_location, 'HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt')\n", "\n", - "# upload data\n", - "data = open(unprocessed_data_location + 'uniprot-cofactor-catalyst.tab').readlines()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# reformat data and write it out\n", - "with open(processed_data_location + 'UNIPROT_PROTEIN_COFACTOR.txt', 'w') as outfile1, open(processed_data_location + 'UNIPROT_PROTEIN_CATALYST.txt', 'w') as outfile2:\n", - " for line in tqdm(data):\n", - " status = line.split('\\t')[1]; upt_id = line.split('\\t')[0]; upt_entry = line.split('\\t')[2]\n", - " pr_id = 'PR_' + line.split('\\t')[3].strip(';')\n", - " # get cofactors\n", - " if 'CHEBI' in line.split('\\t')[4]: \n", - " for i in line.split('\\t')[4].split(';'):\n", - " chebi = i.split('[')[-1].replace(']', '').replace(':', '_')\n", - " outfile1.write(pr_id + '\\t' + chebi + '\\t' + status + '\\t' + upt_id + '\\t' + upt_entry + '\\n')\n", - " # get catalysts\n", - " if 'CHEBI' in line.split('\\t')[5]: \n", - " for i in line.strip('\\n').split('\\t')[5].split(';'):\n", - " chebi = i.split('[')[-1].replace(']', '').replace(':', '_')\n", - " outfile2.write(pr_id + '\\t' + chebi + '\\t' + status + '\\t' + upt_id + '\\t' + upt_entry + '\\n')\n", - " \n", - " " + "# load data\n", + "hpa_gen_ant = pandas.read_csv(unprocessed_data_location + 'HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt', header=None, delimiter='\\t', skiprows=0)\n", + "\n", + "# filter data\n", + "hpa_gen_ant = hpa_gen_ant[hpa_gen_ant[3] != 'No human protein/transcript evidence']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "***\n", - "\n", - "**Cofactor Data** " + " *Merge Identifier Maps*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 109, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>0</th>\n", + " <th>1</th>\n", + " <th>2</th>\n", + " <th>3</th>\n", + " <th>4</th>\n", + " <th>5</th>\n", + " <th>6</th>\n", + " <th>7</th>\n", + " <th>8</th>\n", + " <th>Uniprot_Accession_IDs</th>\n", + " <th>Protein_Ontology_IDs</th>\n", + " <th>anatomy_ids</th>\n", + " <th>ontolgoy_ids</th>\n", + " <th>Gene_Symbols</th>\n", + " <th>Ensembl_Transcript_IDs</th>\n", + " <th>Gene_Type</th>\n", + " <th>Ensembl_Transcript_Type</th>\n", + " <th>Master_Gene_Type</th>\n", + " <th>Master_Transcript_Type</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>ENSG00000121410</td>\n", + " <td>A1BG</td>\n", + " <td>P04217</td>\n", + " <td>Evidence at protein level</td>\n", + " <td>anatomy</td>\n", + " <td>None</td>\n", + " <td>liver</td>\n", + " <td>1234.7</td>\n", + " <td>The Human Protein Atlas</td>\n", + " <td>P04217</td>\n", + " <td>PR_P04217</td>\n", + " <td>liver</td>\n", + " <td>UBERON_0001114</td>\n", + " <td>A1BG</td>\n", + " <td>ensembl_ENST00000595014</td>\n", + " <td>protein-coding</td>\n", + " <td>retained_intron</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>ENSG00000121410</td>\n", + " <td>A1BG</td>\n", + " <td>P04217</td>\n", + " <td>Evidence at protein level</td>\n", + " <td>anatomy</td>\n", + " <td>None</td>\n", + " <td>liver</td>\n", + " <td>1234.7</td>\n", + " <td>The Human Protein Atlas</td>\n", + " <td>P04217</td>\n", + " <td>PR_P04217</td>\n", + " <td>liver</td>\n", + " <td>UBERON_0001114</td>\n", + " <td>A1BG</td>\n", + " <td>ensembl_ENST00000596924</td>\n", + " <td>protein-coding</td>\n", + " <td>processed_transcript</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>ENSG00000121410</td>\n", + " <td>A1BG</td>\n", + " <td>P04217</td>\n", + " <td>Evidence at protein level</td>\n", + " <td>anatomy</td>\n", + " <td>None</td>\n", + " <td>liver</td>\n", + " <td>1234.7</td>\n", + " <td>The Human Protein Atlas</td>\n", + " <td>P04217</td>\n", + " <td>PR_P04217</td>\n", + " <td>liver</td>\n", + " <td>UBERON_0001114</td>\n", + " <td>A1BG</td>\n", + " <td>ensembl_ENST00000263100</td>\n", + " <td>protein-coding</td>\n", + " <td>protein_coding</td>\n", + " <td>protein-coding</td>\n", + " <td>protein-coding</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " 0 1 2 3 4 5 \\\n", + "0 ENSG00000121410 A1BG P04217 Evidence at protein level anatomy None \n", + "1 ENSG00000121410 A1BG P04217 Evidence at protein level anatomy None \n", + "2 ENSG00000121410 A1BG P04217 Evidence at protein level anatomy None \n", + "\n", + " 6 7 8 Uniprot_Accession_IDs \\\n", + "0 liver 1234.7 The Human Protein Atlas P04217 \n", + "1 liver 1234.7 The Human Protein Atlas P04217 \n", + "2 liver 1234.7 The Human Protein Atlas P04217 \n", + "\n", + " Protein_Ontology_IDs anatomy_ids ontolgoy_ids Gene_Symbols \\\n", + "0 PR_P04217 liver UBERON_0001114 A1BG \n", + "1 PR_P04217 liver UBERON_0001114 A1BG \n", + "2 PR_P04217 liver UBERON_0001114 A1BG \n", + "\n", + " Ensembl_Transcript_IDs Gene_Type Ensembl_Transcript_Type \\\n", + "0 ensembl_ENST00000595014 protein-coding retained_intron \n", + "1 ensembl_ENST00000596924 protein-coding processed_transcript \n", + "2 ensembl_ENST00000263100 protein-coding protein_coding \n", + "\n", + " Master_Gene_Type Master_Transcript_Type \n", + "0 protein-coding protein-coding \n", + "1 protein-coding protein-coding \n", + "2 protein-coding protein-coding " + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# load data, print row count, and preview it\n", - "pcp1_data = pandas.read_csv(processed_data_location + 'UNIPROT_PROTEIN_COFACTOR.txt', header=None,\n", - " names=['Protein_Ontology_IDs', 'CHEBI_IDs', 'Status', 'Uniprot_ID', 'Uniprot_Entry_name'],\n", - " delimiter='\\t')\n", + "hpa_gen_ant = hpa_gen_ant.merge(uniprot_pro_map, left_on=2, right_on='Uniprot_Accession_IDs')\n", + "hpa_gen_ant = hpa_gen_ant.merge(anatomy_maps, left_on=6, right_on='anatomy_ids')\n", + "hpa_gen_ant = hpa_gen_ant.merge(symbol_transcript_map, left_on=1, right_on='Gene_Symbols')\n", "\n", - "print('There are {edge_count} protein-cofactor edges'.format(edge_count=len(pcp1_data)))\n", - "pcp1_data.head(n=5)" + "# visualize data\n", + "hpa_gen_ant.head(n=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "***\n", - "\n", - "\n", - "**Catalyst Data** " + "*Create Metadata Dictionary*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 111, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/823001 [00:07<?, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "protein-anatomy PR_P04217-UBERON_0001114 UBERON_0001114\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ - "# load data, print row count, and preview it\n", - "pcp2_data = pandas.read_csv(processed_data_location + 'UNIPROT_PROTEIN_CATALYST.txt', header=None,\n", - " names=['Protein_Ontology_IDs', 'CHEBI_IDs', 'Status', 'Uniprot_ID', 'Uniprot_Entry_name'],\n", - " delimiter='\\t')\n", + "master_metadata_dictionary['edges'].update({'protein-anatomy': {}, 'protein-cell': {}, 'rna-anatomy': {}, 'rna-cell': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(hpa_gen_ant.iterrows(), total=hpa_gen_ant.shape[0]):\n", + " node_key = row['ontolgoy_ids']; anatomy = row[6]; anatomy_type = row[4]; subcell = row[5]\n", + " evidence = [{'HPA_GTEx_Expression_Value': row[7], 'HPA_GTEx_Source': row[8], 'HPA_GTEx_Evidence': row[3]}] \n", + " protein = row['Protein_Ontology_IDs'].rstrip(); rna = row['Ensembl_Transcript_IDs']\n", + " if row[3] == 'Evidence at protein level' and row[4] == 'anatomy':\n", + " node_key2 = protein; edge_key = '{}-{}'.format(node_key2, node_key); edge_type = 'protein-anatomy'\n", + " elif row[3] == 'Evidence at protein level' and row[4] != 'anatomy':\n", + " node_key2 = protein; edge_key = '{}-{}'.format(node_key2, node_key); edge_type = 'protein-cell'\n", + " elif row[3] == 'Evidence at transcript level' and row[4] == 'anatomy':\n", + " node_key2 = rna; edge_key = '{}-{}'.format(node_key2, node_key); edge_type = 'rna-anatomy'\n", + " elif row[3] == 'Evidence at transcript level' and row[4] != 'anatomy':\n", + " node_key2 = rna; edge_key = '{}-{}'.format(node_key2, node_key); edge_type = 'protein-cell'\n", + " else: pass\n", + " \n", + " # add anatomical information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key].keys():\n", + " master_metadata_dictionary['nodes'][node_key][url]['HPA_GTEx_Anatomy'] |= {anatomy}\n", + " master_metadata_dictionary['nodes'][node_key][url]['HPA_GTEx_Anatomy_Type'] |= {anatomy_type}\n", + " master_metadata_dictionary['nodes'][node_key][url]['HPA_GTEx_Subcellular_Location'] |= {subcell}\n", + " else:\n", + " master_metadata_dictionary['nodes'][node_key].update({url: {\n", + " 'HPA_GTEx_Anatomy': {anatomy},\n", + " 'HPA_GTEx_Anatomy_Type': {anatomy_type},\n", + " 'HPA_GTEx_Subcellular_Location': {subcell}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({node_key: {url: {\n", + " 'HPA_GTEx_Anatomy': {anatomy},\n", + " 'HPA_GTEx_Anatomy_Type': {anatomy_type},\n", + " 'HPA_GTEx_Subcellular_Location': {subcell}}}})\n", + "\n", + " # add genomic information\n", + " if node_key2 in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[node_key2]\n", + " if node_key2 in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][node_key2].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][node_key2].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({node_key2: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key2: {'genomic_data': 'None'}})\n", "\n", - "print('There are {edge_count} protein-catalyst edges'.format(edge_count=len(pcp2_data)))\n", - "pcp2_data.head(n=5)" + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'HPA_GTEx_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['HPA_GTEx_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['HPA_GTEx_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'HPA_GTEx_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'HPA_GTEx_Evidence': evidence}}\n", + "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "<br>\n", - "\n", - "***\n", - "***\n", - "### INSTANCE AND/OR SUBCLASS (NON-ONTOLOGY CLASS) METADATA <a class=\"anchor\" id=\"create-instance-metadata\"></a>\n", - "***\n", - "\n", - "**Data Source Wiki Page:** [Dependencies](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies/#node-metadata) \n", - "\n", - "**Purpose:** The goal of this section is to obtain metadata for each non-ontology instance and/or subclass data source and all relations used in the knowledge graph. For **[`Release V2.0.0`](https://github.com/callahantiff/PheKnowLator/wiki/v2.0.0)**, the following are non-ontology instance and/or subclass data and require the compiling of metadata:\n", - "- [Genes](#gene-metadata)\n", - "- [RNA](#rna-metadata)\n", - "- [Variants](#variant-metadata) \n", - "- [Pathways](#pathway-metadata)\n", - "- [Relations](#relations-metadata)\n", - "\n", - "<br>\n", - "\n", - "**Metadata:** The <u>metadata</u> we will gather includes: \n", - "\n", - "| **Metadata Type** | **Definition** | **Example Node** | **Example Node Metadata** | \n", - "| :---: | :---: | :---: | :---: | \n", - "| Label | The primary label or name for the node | `R-HSA-1006173` | \"CFH:Host cell surface\" | \n", - "| Description | A definition or other useful details about the node | `rs794727058` | This `germline` `single nucleotide variant` located on chromosome `5 (GRCh38: NC_000005.10, start/stop positions (126555930/126555930))` with `pathogenic` clinical significance and a last review date of `2/23/2015` (review status: `criteria provided, single submitter`). | \n", - "| Synonym | Alternative terms used for a node | `81399` | \"OR1-1, OR7-21\" | \n", - "\n", - "The metadata information will be used to create the following edges in the knowledge graph: \n", - "- **Label** ➞ node `rdfs:label` \n", - "- **Description** ➞ node `obo:IAO_0000115` description \n", - "- **Synonyms** ➞ node `oboInOwl:hasExactSynonym` synonym \n", - "\n", - "<br>\n", - "\n", - "*<b>NOTE.</b> All node metadata are written to the `node_data` directory as a `pickled` dictionary called `node_metadata_dict.pkl`. The algorithm will look for this dictionary in the `node_data` directory and if it is not there, then no node metadata will be created.*\n", - "\n", - "<br>\n", - "\n", - "### Prepare Metadata Dictionaries\n", "***\n", "\n", - "**Purpose:** To create the resources needed in order to create metadata dictionaries, which are in turn used to obtain metadata for instance and/or subclass data nodes. This process has the following steps:\n", - "\n", - "**1. [Generate Metadata Dictionaries](#generate-metadata-dictionaries):** In order to efficiently obtain metadata for all non-ontology instance and/or subclass data nodes and all relations, we first read in the data for each type (i.e. genes, rna, pathways, variants, and relations) and convert them into a dictionary. Then, each metadata dictionary is merged together and saved to a `master_metadata_dictionary`, keyed by identifier.\n", - " - <u>Input Datasets</u>: \n", - " - Genes ➞ `Homo_sapiens.gene_info` \n", - " - RNA ➞ `ensembl_identifier_data_cleaned.txt` \n", - " - Pathways ➞ [`reactome2py API`](https://github.com/reactome/reactome2py) ; `ReactomePathways.txt`; `gene_association.reactome.gz`; `ChEBI2Reactome_All_Levels.txt`; `kegg_reactome.csv` \n", - " - Variants ➞ `variant_summary.txt` \n", - " - Relations ➞ `ro_with_imports.owl` \n", - " \n", - "Example Metadata Dictionary Output:\n", - "\n", - "```python\n", - "{\n", - " 'nodes': {\n", - " 'http://www.ncbi.nlm.nih.gov/gene/1': {\n", - " 'Label': 'A1BG',\n", - " 'Description': \"A1BG has locus group protein-coding' and is located on chromosome 19 (19q13.43).\",\n", - " 'Synonym': 'HYST2477alpha-1B-glycoprotein|HEL-S-163pA|ABG|A1B|GAB'} ... },\n", - " 'relations': {\n", - " 'http://purl.obolibrary.org/obo/RO_0002533': {\n", - " 'Label': 'sequence atomic unit',\n", - " 'Description': 'Any individual unit of a collection of like units arranged in a linear order',\n", - " 'Synonym': 'None'} ... }\n", - "}\n", - "```\n", + "#### `UNIPROT_PROTEIN_CATALYST.txt` <a class=\"anchor\" id=\"uniprot-catalyst\"></a>\n", "\n", - "<br>\n", + "**Edges:** \n", + "- `protein-catalyst` \n", "\n", - "**2. [Write Metadata Files](#write-metadata-files):** The `master_metadata_dictionary` dictionary from _Step 1_ is `pickled` and saved to the `resources/node_data/` directory.\n", + "This chunk process the [`UNIPROT_PROTEIN_CATALYST.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_CATALYST.txt) file and obtains the following node and edge metadata: \n", + " \n", + "- **Edges:** \n", + " - `Status`: A string to indicate the status of the entry in Uniprot. " + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>0</th>\n", + " <th>1</th>\n", + " <th>2</th>\n", + " <th>3</th>\n", + " <th>4</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>PR_Q9NY84</td>\n", + " <td>CHEBI_16753</td>\n", + " <td>reviewed</td>\n", + " <td>Q9NY84</td>\n", + " <td>VNN3_HUMAN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>PR_Q9NY84</td>\n", + " <td>CHEBI_15377</td>\n", + " <td>reviewed</td>\n", + " <td>Q9NY84</td>\n", + " <td>VNN3_HUMAN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>PR_Q9NY84</td>\n", + " <td>CHEBI_29032</td>\n", + " <td>reviewed</td>\n", + " <td>Q9NY84</td>\n", + " <td>VNN3_HUMAN</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " 0 1 2 3 4\n", + "0 PR_Q9NY84 CHEBI_16753 reviewed Q9NY84 VNN3_HUMAN\n", + "1 PR_Q9NY84 CHEBI_15377 reviewed Q9NY84 VNN3_HUMAN\n", + "2 PR_Q9NY84 CHEBI_29032 reviewed Q9NY84 VNN3_HUMAN" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# download data\n", + "url = 'https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_CATALYST.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'UNIPROT_PROTEIN_CATALYST.txt'):\n", + " data_downloader(url, unprocessed_data_location, 'UNIPROT_PROTEIN_CATALYST.txt')\n", "\n", - "<br>\n", + "# load data\n", + "upt_prot_cat = pandas.read_csv(unprocessed_data_location + 'UNIPROT_PROTEIN_CATALYST.txt', header=None, delimiter='\\t', skiprows=0)\n", "\n", - "***" + "# visualize data\n", + "upt_prot_cat.head(n=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Generate Metadata Dictionaries <a class=\"anchor\" id=\"generate-metadata-dictionaries\"></a>\n", - "In this step, the goal is to create a metadata dictionary for each node type that does not rely on API data. In this case, only the **Gene**, **RNA**, and **Variant** nodes require data that is not from an API.\n" + "*Create Metadata Dictionary*" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/95058 [00:00<?, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "protein-catalyst PR_Q9NY84-CHEBI_16753 PR_Q9NY84\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "master_metadata_dictionary['edges'].update({'protein-catalyst': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(upt_prot_cat.iterrows(), total=upt_prot_cat.shape[0]):\n", + " node_key = row[0]; chebi = row[1]; evidence = [{'Uniprot_Status': row[2]}] \n", + " edge_key = '{}-{}'.format(node_key, chebi); edge_type = 'protein-catalyst'\n", + " \n", + " # add catalyst information\n", + " if chebi in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][chebi].keys():\n", + " master_metadata_dictionary['nodes'][chebi][url]['Uniprot_CHEBI'] |= {chebi}\n", + " else: master_metadata_dictionary['nodes'][chebi].update({url: {'Uniprot_CHEBI': {chebi}}})\n", + " else: master_metadata_dictionary['nodes'].update({chebi: {url: {'Uniprot_CHEBI': {chebi}}}})\n", + " \n", + " # add genomic information\n", + " if node_key in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[node_key]\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][node_key].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][node_key].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", + "\n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'Uniprot_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['Uniprot_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Uniprot_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Uniprot_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Uniprot_Evidence': evidence}}\n" ] }, { @@ -5506,60 +10844,173 @@ "source": [ "***\n", "\n", - "#### Genes Metadata Dictionary <a class=\"anchor\" id=\"gene-metadata\"></a>\n", + "#### `UNIPROT_PROTEIN_COFACTOR.txt` <a class=\"anchor\" id=\"uniprot-cofactor\"></a>\n", "\n", - "The nested dictionary of gene metadata is created by looping over the merged data described in the prior column. The `keys` of the dictionary are `Entrez gene identifiers` and the `values` are dictionaries for each metadata type." + "**Edges:** \n", + "- `protein-cofactor` \n", + "\n", + "This chunk process the [`UNIPROT_PROTEIN_COFACTOR.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_COFACTOR.txt) file and obtains the following node and edge metadata: \n", + " \n", + "- **Edges:** \n", + " - `Status`: A string to indicate the status of the entry in Uniprot. " ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 125, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>0</th>\n", + " <th>1</th>\n", + " <th>2</th>\n", + " <th>3</th>\n", + " <th>4</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>PR_Q5D1E8</td>\n", + " <td>CHEBI_18420</td>\n", + " <td>reviewed</td>\n", + " <td>Q5D1E8</td>\n", + " <td>ZC12A_HUMAN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>PR_Q9NQH7</td>\n", + " <td>CHEBI_29035</td>\n", + " <td>reviewed</td>\n", + " <td>Q9NQH7</td>\n", + " <td>XPP3_HUMAN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>PR_Q96TA2</td>\n", + " <td>CHEBI_29105</td>\n", + " <td>reviewed</td>\n", + " <td>Q96TA2</td>\n", + " <td>YMEL1_HUMAN</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " 0 1 2 3 4\n", + "0 PR_Q5D1E8 CHEBI_18420 reviewed Q5D1E8 ZC12A_HUMAN\n", + "1 PR_Q9NQH7 CHEBI_29035 reviewed Q9NQH7 XPP3_HUMAN\n", + "2 PR_Q96TA2 CHEBI_29105 reviewed Q96TA2 YMEL1_HUMAN" + ] + }, + "execution_count": 125, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# entrez gene data\n", - "entrez_gene_data = pandas.read_csv(unprocessed_data_location + 'Homo_sapiens.gene_info', header=0, delimiter='\\t', low_memory=False)\n", + "# download data\n", + "url = 'https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_COFACTOR.txt'\n", + "if not os.path.exists(unprocessed_data_location + 'UNIPROT_PROTEIN_COFACTOR.txt'):\n", + " data_downloader(url, unprocessed_data_location, 'UNIPROT_PROTEIN_COFACTOR.txt')\n", "\n", - "# remove all rows that are not human\n", - "entrez_gene_data = entrez_gene_data.loc[entrez_gene_data['#tax_id'].apply(lambda x: x == 9606)]\n", + "# load data\n", + "upt_prot_cof = pandas.read_csv(unprocessed_data_location + 'UNIPROT_PROTEIN_COFACTOR.txt', header=None, delimiter='\\t', skiprows=0)\n", "\n", - "# replace NaN and '-' with 'None'\n", - "entrez_gene_data.fillna('None', inplace=True)\n", - "entrez_gene_data.replace('-','None', inplace=True, regex=False)" + "# visualize data\n", + "upt_prot_cof.head(n=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Create Metadata Dictionary*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 126, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/10630 [00:00<?, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "protein-catalyst PR_Q5D1E8-CHEBI_18420 PR_Q5D1E8\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ - "# create metadata\n", - "genes, lab, desc, syn = [], [], [], []\n", - "for idx, row in tqdm(entrez_gene_data.iterrows(), total=entrez_gene_data.shape[0]):\n", - " gene_id, sym, defn, gene_type = row['GeneID'], row['Symbol'], row['description'], row['type_of_gene']\n", - " chrom, map_loc, s1, s2 = row['chromosome'], row['map_location'], row['Synonyms'], row['Other_designations']\n", - " if gene_id != 'None':\n", - " genes.append('http://www.ncbi.nlm.nih.gov/gene/' + str(gene_id))\n", - " if sym != 'None' or sym != '': lab.append(sym)\n", - " else: lab.append('Entrez_ID:' + gene_id)\n", - " if 'None' not in [defn, gene_type, chrom, map_loc]:\n", - " desc_str = \"{} has locus group '{}' and is located on chromosome {} ({}).\"\n", - " desc.append(desc_str.format(sym, gene_type, chrom, map_loc))\n", - " else: desc.append(\"{} locus group '{}'.\".format(sym, gene_type))\n", - " if s1 != 'None' and s2 != 'None': syn.append('|'.join(set([x for x in (s1 + s2).split('|') if x != 'None' or x != ''])))\n", - " elif s1 != 'None': syn.append('|'.join(set([x for x in s1.split('|') if x != 'None' or x != ''])))\n", - " elif s2 != 'None': syn.append('|'.join(set([x for x in s2.split('|') if x != 'None' or x != ''])))\n", - " else: syn.append('None')\n", + "master_metadata_dictionary['edges'].update({'protein-cofactor': {}})\n", "\n", - "# combine into new data frame\n", - "metadata = pandas.DataFrame(list(zip(genes, lab, desc, syn)), columns=['ID', 'Label', 'Description', 'Synonym'])\n", - "metadata = metadata.astype(str)\n", - "metadata.drop_duplicates(subset='ID', keep='first', inplace=True)\n", + "# create dictionary\n", + "for idx, row in tqdm(upt_prot_cof.iterrows(), total=upt_prot_cof.shape[0]):\n", + " node_key = row[0]; chebi = row[1]; evidence = [{'Uniprot_Status': row[2]}] \n", + " edge_key = '{}-{}'.format(node_key, chebi); edge_type = 'protein-catalyst'\n", + " \n", + " # add catalyst information\n", + " if chebi in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][chebi].keys():\n", + " master_metadata_dictionary['nodes'][chebi][url]['Uniprot_CHEBI'] |= {chebi}\n", + " else: master_metadata_dictionary['nodes'][chebi].update({url: {'Uniprot_CHEBI': {chebi}}})\n", + " else: master_metadata_dictionary['nodes'].update({chebi: {url: {'Uniprot_CHEBI': {chebi}}}})\n", + " \n", + " # add genomic information\n", + " if node_key in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[node_key]\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][node_key].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][node_key].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", "\n", - "# convert df to dictionary\n", - "metadata.set_index('ID', inplace=True)\n", - "gene_metadata_dict = metadata.to_dict('index')" + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'Uniprot_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['Uniprot_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Uniprot_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Uniprot_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Uniprot_Evidence': evidence}}\n" ] }, { @@ -5568,137 +11019,266 @@ "source": [ "***\n", "\n", - "#### RNA Metadata Dictionary <a class=\"anchor\" id=\"rna-metadata\"></a>\n", + "#### `9606.protein.links.v11.0.txt.gz` <a class=\"anchor\" id=\"protein-protein\"></a>\n", "\n", - "The nested dictionary of rna metadata is created by looping over the cleaned human [Ensembl](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#ensembl) gene, RNA, and protein identifier data set (`ensembl_identifier_data_cleaned.txt`). The `keys` of the dictionary are `Ensembl transcript identifiers` and the `values` are dictionaries for each metadata type." + "**Edges:** \n", + "- `protein-protein` \n", + "\n", + "**Identifier Maps:** \n", + "- Proteins: [STRING_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/STRING_PRO_ONTOLOGY_MAP.txt) \n", + "\n", + "This chunk process the [`9606.protein.links.v11.0.txt.gz`](https://stringdb-static.org/download/protein.links.v11.0/9606.protein.links.v11.0.txt.gz) file and obtains the following node and edge metadata: \n", + " \n", + "- **Edges:** \n", + " - `combined_score`: The combined score is computed by combining the probabilities from the different evidence channels and corrected for the probability of randomly observing an interaction. Scores range from 0-1000. For a more detailed description please see PMID:15608232. " ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 131, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>protein1</th>\n", + " <th>protein2</th>\n", + " <th>combined_score</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>9606.ENSP00000000233</td>\n", + " <td>9606.ENSP00000272298</td>\n", + " <td>490</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>9606.ENSP00000000233</td>\n", + " <td>9606.ENSP00000253401</td>\n", + " <td>198</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>9606.ENSP00000000233</td>\n", + " <td>9606.ENSP00000401445</td>\n", + " <td>159</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " protein1 protein2 combined_score\n", + "0 9606.ENSP00000000233 9606.ENSP00000272298 490\n", + "1 9606.ENSP00000000233 9606.ENSP00000253401 198\n", + "2 9606.ENSP00000000233 9606.ENSP00000401445 159" + ] + }, + "execution_count": 131, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# load data\n", - "rna_gene_data = pandas.read_csv(processed_data_location + 'ensembl_identifier_data_cleaned.txt', header=0, delimiter='\\t', low_memory=False)\n", - "\n", - "# remove rows without identifiers\n", - "rna_gene_data = rna_gene_data.loc[rna_gene_data['transcript_stable_id'].apply(lambda x: x != 'None')]\n", - "\n", - "# remove unneeded columns\n", - "rna_gene_data.drop(['ensembl_gene_id', 'symbol', 'protein_stable_id', 'uniprot_id', 'master_transcript_type',\n", - " 'entrez_id', 'ensembl_gene_type', 'master_gene_type', 'symbol'], axis=1, inplace=True)\n", - "\n", - "# remove duplicates\n", - "rna_gene_data.drop_duplicates(subset=['transcript_stable_id', 'transcript_name', 'ensembl_transcript_type'], keep='first', inplace=True)\n", + "# download data\n", + "url = 'https://stringdb-static.org/download/protein.links.v11.0/9606.protein.links.v11.0.txt.gz'\n", + "if not os.path.exists(unprocessed_data_location + '9606.protein.links.v11.0.txt'):\n", + " data_downloader(url, unprocessed_data_location, '9606.protein.links.v11.0.txt')\n", "\n", - "# replace NaN with 'None'\n", - "rna_gene_data.fillna('None', inplace=True)" + "# load data\n", + "stg_prot_prot = pandas.read_csv(unprocessed_data_location + '9606.protein.links.v11.0.txt', header=0, delimiter=' ', skiprows=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " *Merge Identifier Maps*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 133, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>protein1</th>\n", + " <th>protein2</th>\n", + " <th>combined_score</th>\n", + " <th>STRING_IDs_x</th>\n", + " <th>Protein_Ontology_IDs_x</th>\n", + " <th>STRING_IDs_y</th>\n", + " <th>Protein_Ontology_IDs_y</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>9606.ENSP00000000233</td>\n", + " <td>9606.ENSP00000272298</td>\n", + " <td>490</td>\n", + " <td>9606.ENSP00000000233</td>\n", + " <td>PR_P84085</td>\n", + " <td>9606.ENSP00000272298</td>\n", + " <td>PR_P0DP24</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>9606.ENSP00000001008</td>\n", + " <td>9606.ENSP00000272298</td>\n", + " <td>196</td>\n", + " <td>9606.ENSP00000001008</td>\n", + " <td>PR_Q02790</td>\n", + " <td>9606.ENSP00000272298</td>\n", + " <td>PR_P0DP24</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>9606.ENSP00000005178</td>\n", + " <td>9606.ENSP00000272298</td>\n", + " <td>155</td>\n", + " <td>9606.ENSP00000005178</td>\n", + " <td>PR_Q16654</td>\n", + " <td>9606.ENSP00000272298</td>\n", + " <td>PR_P0DP24</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " protein1 protein2 combined_score \\\n", + "0 9606.ENSP00000000233 9606.ENSP00000272298 490 \n", + "1 9606.ENSP00000001008 9606.ENSP00000272298 196 \n", + "2 9606.ENSP00000005178 9606.ENSP00000272298 155 \n", + "\n", + " STRING_IDs_x Protein_Ontology_IDs_x STRING_IDs_y \\\n", + "0 9606.ENSP00000000233 PR_P84085 9606.ENSP00000272298 \n", + "1 9606.ENSP00000001008 PR_Q02790 9606.ENSP00000272298 \n", + "2 9606.ENSP00000005178 PR_Q16654 9606.ENSP00000272298 \n", + "\n", + " Protein_Ontology_IDs_y \n", + "0 PR_P0DP24 \n", + "1 PR_P0DP24 \n", + "2 PR_P0DP24 " + ] + }, + "execution_count": 133, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# create metadata\n", - "rna, lab, desc, syn = [], [], [], []\n", - "for idx, row in tqdm(rna_gene_data.iterrows(), total=rna_gene_data.shape[0]):\n", - " rna_id, ent_type, nme = row['transcript_stable_id'], row['ensembl_transcript_type'], row['transcript_name']\n", - " rna.append('https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=' + rna_id)\n", - " if nme != 'None':\n", - " lab.append(nme)\n", - " else:\n", - " lab.append('Ensembl_Transcript_ID:' + rna_id)\n", - " nme = 'Ensembl_Transcript_ID:' + rna_id\n", - " if ent_type != 'None': desc.append(\"Transcript {} is classified as type '{}'.\".format(nme, ent_type))\n", - " else: desc.append('None')\n", - " syn.append('None')\n", + "stg_prot_prot = stg_prot_prot.merge(string_pro_map, left_on='protein1', right_on='STRING_IDs')\n", + "stg_prot_prot = stg_prot_prot.merge(string_pro_map, left_on='protein2', right_on='STRING_IDs')\n", "\n", - "# combine into new data frame\n", - "metadata = pandas.DataFrame(list(zip(rna, lab, desc, syn)), columns=['ID', 'Label', 'Description', 'Synonym'])\n", - "metadata = metadata.astype(str)\n", - "metadata.drop_duplicates(subset='ID', keep='first', inplace=True)\n", - "\n", - "# convert df to dictionary\n", - "metadata.set_index('ID', inplace=True)\n", - "rna_metadata_dict = metadata.to_dict('index')" + "# visualize data\n", + "stg_prot_prot.head(n=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "***\n", - "\n", - "#### Variant Metadata Dictionary <a class=\"anchor\" id=\"variant-metadata\"></a> \n", - "\n", - "The nested dictionary of rna metadata is created by looping over the human [ClinVar Variant](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#clinvar) identifier data set (`variant_summary.txt`). The `keys` of the dictionary are `dbSNP identifiers` and the `values` are dictionaries for each metadata type." + "*Create Metadata Dictionary*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 136, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/8334872 [00:01<?, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "protein-protein PR_P84085-PR_P0DP24 PR_P0DP24\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ - "# download data\n", - "url = 'ftp://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz'\n", - "if not os.path.exists(unprocessed_data_location + 'variant_summary.txt'):\n", - " data_downloader(url, unprocessed_data_location)\n", - "\n", - "# load data\n", - "var_data = pandas.read_csv(unprocessed_data_location + 'variant_summary.txt', header=0, delimiter='\\t', low_memory=False)\n", - "\n", - "# remove rows without identifiers\n", - "var_data = var_data.loc[var_data['Assembly'].apply(lambda x: x == 'GRCh38')]\n", - "var_data = var_data.loc[var_data['RS# (dbSNP)'].apply(lambda x: x != -1)]\n", - "\n", - "# de-dup data\n", - "var_metadata = var_data[['#AlleleID', 'Type', 'Name', 'ClinicalSignificance', 'RS# (dbSNP)', 'Origin',\n", - " 'ChromosomeAccession', 'Chromosome', 'Start', 'Stop', 'ReferenceAllele',\n", - " 'Assembly', 'AlternateAllele','Cytogenetic', 'ReviewStatus', 'LastEvaluated']] \n", - "\n", - "# replace NaN with 'None'\n", - "var_metadata.replace('na', 'None', inplace=True)\n", - "var_metadata.fillna('None', inplace=True)\n", + "master_metadata_dictionary['edges'].update({'protein-protein': {}})\n", "\n", - "# remove duplicate dbSNP ids by choosing the most recent reviewed variant\n", - "var_metadata.sort_values('LastEvaluated', ascending=False, inplace=True)\n", - "var_metadata.drop_duplicates(subset='RS# (dbSNP)', keep='first', inplace=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# create metadata\n", - "variant, label, desc, syn = [], [], [], []\n", - "for idx, row in tqdm(var_metadata.iterrows(), total=var_metadata.shape[0]):\n", - " var_id, lab = row['RS# (dbSNP)'], row['Name']\n", - " if var_id != 'None':\n", - " variant.append('https://www.ncbi.nlm.nih.gov/snp/rs' + str(var_id))\n", - " if lab != 'None': label.append(lab)\n", - " else: label.append('dbSNP_ID:rs' + str(var_id))\n", - " sent = \"This variant is a {} {} located on chromosome {} ({}, start:{}/stop:{} positions, \" +\\\n", - " \"cytogenetic location:{}) and has clinical significance '{}'. \" +\\\n", - " \"This entry is for the {} and was last reviewed on {} with review status '{}'.\"\n", - " desc.append(sent.format(row['Origin'].replace(';', '/'), row['Type'].replace(';', '/'), row['Chromosome'], row['ChromosomeAccession'],\n", - " row['Start'], row['Stop'], row['Cytogenetic'], row['ClinicalSignificance'],\n", - " row['Assembly'], row['LastEvaluated'], row['ReviewStatus']).replace('None', 'UNKNOWN'))\n", - " syn.append('None')\n", + "# create dictionary\n", + "for idx, row in tqdm(stg_prot_prot.iterrows(), total=stg_prot_prot.shape[0]):\n", + " proteins = [row['Protein_Ontology_IDs_x'], row['Protein_Ontology_IDs_y']]; score = row['combined_score']; gene_info = []\n", + " edge_key = '{}-{}'.format(row['Protein_Ontology_IDs_x'], row['Protein_Ontology_IDs_y']); edge_type = 'protein-protein' \n", " \n", - "# combine into new data frame\n", - "var_metadata_final = pandas.DataFrame(list(zip(variant, label, desc, syn)), columns =['ID', 'Label', 'Description', 'Synonym'])\n", - "var_metadata_final.drop_duplicates(subset=None, keep='first', inplace=True)\n", - "var_metadata_final = var_metadata_final.astype(str)\n", - "\n", - "# convert df to dictionary\n", - "var_metadata_final.set_index('ID', inplace=True)\n", - "var_metadata_dict = var_metadata_final.to_dict('index') " + " for node_key in proteins:\n", + " if node_key in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[node_key]\n", + " else: genomic_info_dict = None\n", + " \n", + " # add genomic information\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][node_key].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][node_key].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}}) \n", + " \n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'String_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['String_Evidence'] = score\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'String_Evidence': score}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'String_Evidence': score}}\n", + "\n" ] }, { @@ -5707,155 +11287,346 @@ "source": [ "***\n", "\n", - "#### Pathway Metadata Dictionary <a class=\"anchor\" id=\"pathway-metadata\"></a> \n", + "#### `curated_gene_disease_associations.tsv` <a class=\"anchor\" id=\"gene-phen\"></a>\n", "\n", - "The nested dictionary of pathway metadata is created by looping over the human [Reactome Pathway Database](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#reactome-pathway-database) identifier data set (`ReactomePathways.txt`); Reactome-Gene Association data (`gene_association.reactome.gz`), and Reactome-ChEBI data (`ChEBI2Reactome_All_Levels.txt`). The `keys` of the dictionary are `Reactome identifiers` and the `values` are dictionaries for each metadata type." + "**Edges:** \n", + "- `gene-disease` \n", + "- `gene-phenotype` \n", + "\n", + "**Identifier Maps:** \n", + "- Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt)\n", + "- Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) \n", + "\n", + "This chunk process the [curated_gene_disease_associations.tsv](https://www.disgenet.org/static/disgenet_ap1/files/downloads/curated_gene_disease_associations.tsv.gz) file and obtains the following node and edge metadata: \n", + "- **Nodes:** \n", + " - `diseaseId`: A string containing the concept's database cross-reference, which is formatted as DB:ID. If not, a MeSH or OMIM identifier. Variable is provided as a string with the \"MESH\" or \"OMIM\" prefix in all caps. \n", + " - `diseaseName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. \n", + " - `diseaseSematicType`: A string containing a high-level grouper or typing variable for the disease. \n", + " - `diseaseClass`: A \";\"-delimnited list of ICD codes that can be used to classify the disease. \n", + "- **Edges:** \n", + " - `DSI`: The Disease Similarity Index ranges from from 0.25 to 1. It is calculated as: DSI = log2(# diseases assoc with gene/total # of diseases in DisGeNET) / log2(1/total # of diseases in DisGeNET) \n", + " - `DPI`: The Disease Pleiotropy Index ranges from 0 to 1. it is calculated as: DPI = (# of MeSH disease classes of disease assoc with gene/total # of MeSH disease classes)*100. \n", + " - `score`: The score range from 0 to 1, and take into account the number and type of sources (level of curation, model organisms), and the number of publications supporting the association. \n", + " - `EI`: The Evidence Index(EL) is a metric developed by ClinGen that measures the strength of evidence of a gene-disease relationship that correlates to a qualitative classification: \"Definitive\", \"Strong\", \"Moderate\", \"Limited\", \"Disputed\" (Strande et al., 2017). EI = 1 indicates that all the publications support the GDA or the VDA, while EI < 1 indicates that there are publications that assert that there is no association between the gene/variants and the disease. If the gene/variant has no EI value, it indicates that the index has not been computed for this association. It is calculated as: EI = (# positive pubs/total # of pubs). \n", + " - `YearInitial`: First time that the association was reported. \n", + " - `YearFinal`: Last time that the association was reported. \n", + " - `NofPmids`: Count of associated Pubmed IDs. \n", + " - `NofSnps`: Count of associated SNPs. \n", + " - `Source`: The original source reporting the Gene-Disease Association. " ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 154, "metadata": {}, "outputs": [], "source": [ - "# download reactome pathways data\n", - "url = 'https://reactome.org/download/current/ReactomePathways.txt'\n", - "if not os.path.exists(unprocessed_data_location + 'ReactomePathways.txt'):\n", - " data_downloader(url, unprocessed_data_location)\n", - "# load data\n", - "reactome_pathways = pandas.read_csv(unprocessed_data_location + 'ReactomePathways.txt', header=None, delimiter='\\t', low_memory=False)\n", - "reactome_pathways = reactome_pathways.loc[reactome_pathways[2].apply(lambda x: x == 'Homo sapiens')] \n", + "# download data\n", + "url = 'https://www.disgenet.org/static/disgenet_ap1/files/downloads/curated_gene_disease_associations.tsv.gz'\n", + "if not os.path.exists(unprocessed_data_location + 'curated_gene_disease_associations.tsv'):\n", + " data_downloader(url, unprocessed_data_location, 'curated_gene_disease_associations.tsv')\n", "\n", - "# reactome gene association data\n", - "url = 'https://reactome.org/download/current/gene_association.reactome.gz'\n", - "if not os.path.exists(unprocessed_data_location + 'gene_association.reactome'):\n", - " data_downloader(url, unprocessed_data_location)\n", "# load data\n", - "reactome_pathways2 = pandas.read_csv(unprocessed_data_location + 'gene_association.reactome', header=None, delimiter='\\t', skiprows=3, low_memory=False)\n", - "reactome_pathways2 = reactome_pathways2.loc[reactome_pathways2[12].apply(lambda x: x == 'taxon:9606')]\n", - "reactome_pathways2[5].str.replace('REACTOME:','', inplace=True, regex=True) \n", + "dgt_dis_gene = pandas.read_csv(unprocessed_data_location + 'curated_gene_disease_associations.tsv', header=0, delimiter='\\t', skiprows=0)\n", + "dgt_dis_gene = dgt_dis_gene[dgt_dis_gene['diseaseType'] != 'group']\n", "\n", - "# reactome CHEBI data\n", - "url = 'https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt'\n", - "if not os.path.exists(unprocessed_data_location + 'ChEBI2Reactome_All_Levels.txt'):\n", - " data_downloader(url, unprocessed_data_location)\n", - "# load data\n", - "reactome_pathways3 = pandas.read_csv(unprocessed_data_location + 'ChEBI2Reactome_All_Levels.txt', header=None, delimiter='\\t', low_memory=False)\n", - "# remove all non-human pathways and save as list\n", - "reactome_pathways3 = reactome_pathways3.loc[reactome_pathways3[5].apply(lambda x: x == 'Homo sapiens')] " + "# fix variable typing\n", + "dgt_dis_gene['YearInitial'] = dgt_dis_gene['YearInitial'].astype('Int64')\n", + "dgt_dis_gene['YearFinal'] = dgt_dis_gene['YearFinal'].astype('Int64')\n", + "\n", + "# fix prefix\n", + "dgt_dis_gene['geneId'] = 'NCBIGene_' + dgt_dis_gene['geneId'].astype('str')\n" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "# get metadata\n", - "nodes = list(set(reactome_pathways[0]) | set(reactome_pathways2[5]) | set(reactome_pathways3[1]))\n", - "pathway_metadata_final = metadata_api_mapper(nodes)\n", - "\n", - "# update dictionary\n", - "pathway_metadata_final['ID'] = pathway_metadata_final['ID'].map('https://reactome.org/content/detail/{}'.format)\n", - "pathway_metadata_final.set_index('ID', inplace=True)\n", - "\n", - "# convert df to dictionary\n", - "pathway_metadata_dict = pathway_metadata_final.to_dict('index') " + "*Merge Identifier Maps*" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 155, "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>geneId</th>\n", + " <th>geneSymbol</th>\n", + " <th>DSI</th>\n", + " <th>DPI</th>\n", + " <th>diseaseId</th>\n", + " <th>diseaseName</th>\n", + " <th>diseaseType</th>\n", + " <th>diseaseClass</th>\n", + " <th>diseaseSemanticType</th>\n", + " <th>score</th>\n", + " <th>EI</th>\n", + " <th>YearInitial</th>\n", + " <th>YearFinal</th>\n", + " <th>NofPmids</th>\n", + " <th>NofSnps</th>\n", + " <th>source</th>\n", + " <th>Disease_IDs_x</th>\n", + " <th>MONDO_IDs</th>\n", + " <th>Disease_IDs_y</th>\n", + " <th>HP_IDs</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>NCBIGene_2</td>\n", + " <td>A2M</td>\n", + " <td>0.529</td>\n", + " <td>0.769</td>\n", + " <td>C0002395</td>\n", + " <td>Alzheimer's Disease</td>\n", + " <td>disease</td>\n", + " <td>C10;F03</td>\n", + " <td>Disease or Syndrome</td>\n", + " <td>0.5</td>\n", + " <td>0.769</td>\n", + " <td>1998</td>\n", + " <td>2018</td>\n", + " <td>3</td>\n", + " <td>0</td>\n", + " <td>CTD_human</td>\n", + " <td>C0002395</td>\n", + " <td>MONDO_0004975</td>\n", + " <td>C0002395</td>\n", + " <td>HP_0002511</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>NCBIGene_43</td>\n", + " <td>ACHE</td>\n", + " <td>0.445</td>\n", + " <td>0.885</td>\n", + " <td>C0002395</td>\n", + " <td>Alzheimer's Disease</td>\n", + " <td>disease</td>\n", + " <td>C10;F03</td>\n", + " <td>Disease or Syndrome</td>\n", + " <td>0.4</td>\n", + " <td>0.985</td>\n", + " <td>1991</td>\n", + " <td>2020</td>\n", + " <td>2</td>\n", + " <td>0</td>\n", + " <td>CTD_human</td>\n", + " <td>C0002395</td>\n", + " <td>MONDO_0004975</td>\n", + " <td>C0002395</td>\n", + " <td>HP_0002511</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>NCBIGene_102</td>\n", + " <td>ADAM10</td>\n", + " <td>0.489</td>\n", + " <td>0.846</td>\n", + " <td>C0002395</td>\n", + " <td>Alzheimer's Disease</td>\n", + " <td>disease</td>\n", + " <td>C10;F03</td>\n", + " <td>Disease or Syndrome</td>\n", + " <td>0.7</td>\n", + " <td>0.986</td>\n", + " <td>2000</td>\n", + " <td>2019</td>\n", + " <td>1</td>\n", + " <td>1</td>\n", + " <td>CTD_human</td>\n", + " <td>C0002395</td>\n", + " <td>MONDO_0004975</td>\n", + " <td>C0002395</td>\n", + " <td>HP_0002511</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " geneId geneSymbol DSI DPI diseaseId diseaseName \\\n", + "0 NCBIGene_2 A2M 0.529 0.769 C0002395 Alzheimer's Disease \n", + "1 NCBIGene_43 ACHE 0.445 0.885 C0002395 Alzheimer's Disease \n", + "2 NCBIGene_102 ADAM10 0.489 0.846 C0002395 Alzheimer's Disease \n", + "\n", + " diseaseType diseaseClass diseaseSemanticType score EI YearInitial \\\n", + "0 disease C10;F03 Disease or Syndrome 0.5 0.769 1998 \n", + "1 disease C10;F03 Disease or Syndrome 0.4 0.985 1991 \n", + "2 disease C10;F03 Disease or Syndrome 0.7 0.986 2000 \n", + "\n", + " YearFinal NofPmids NofSnps source Disease_IDs_x MONDO_IDs \\\n", + "0 2018 3 0 CTD_human C0002395 MONDO_0004975 \n", + "1 2020 2 0 CTD_human C0002395 MONDO_0004975 \n", + "2 2019 1 1 CTD_human C0002395 MONDO_0004975 \n", + "\n", + " Disease_IDs_y HP_IDs \n", + "0 C0002395 HP_0002511 \n", + "1 C0002395 HP_0002511 \n", + "2 C0002395 HP_0002511 " + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "***\n", - "\n", - "#### Relations Metadata Dictionary <a class=\"anchor\" id=\"relations-metadata\"></a> \n", + "dgt_dis_gene = dgt_dis_gene.merge(disease_maps, left_on='diseaseId', right_on='Disease_IDs')\n", + "dgt_dis_gene = dgt_dis_gene.merge(phenotype_maps, left_on='diseaseId', right_on='Disease_IDs')\n", "\n", - "The nested dictionary of relation metadata is created by looping over the human [Relations Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#relations-ontology) identifier data set (`ro_with_imports.owl`). The `keys` of the dictionary are `Relations Ontology identifiers` and the `values` are dictionaries for each metadata type." + "# visualize data\n", + "dgt_dis_gene.head(n=3)" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "# download ontology\n", - "if not os.path.exists(unprocessed_data_location + 'ro_with_imports.owl'):\n", - " command = '{} {} --merge-import-closure -o {}'\n", - " os.system(command.format(owltools_location, 'http://purl.obolibrary.org/obo/ro.owl',\n", - " unprocessed_data_location + 'ro_with_imports.owl'))\n", - "# load graph\n", - "ro_graph = Graph().parse(unprocessed_data_location + 'ro_with_imports.owl')\n", - "print('There are {} edges in the ontology (date:{})'.format(len(ro_graph), datetime.datetime.now().strftime('%m/%d/%Y')))" + "*Create Metadata Dictionary*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 156, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/13011 [00:00<?, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "gene-disease NCBIGene_2-MONDO_0004975 NCBIGene_2\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ - "# get metadata\n", - "relation_metadata_dict, obo = {}, Namespace('http://purl.obolibrary.org/obo/')\n", - "\n", - "# get ontology information\n", - "cls = [x for x in gets_ontology_classes(ro_graph) if '/RO_' in str(x)] +\\\n", - " [x for x in gets_object_properties(ro_graph) if '/RO_' in str(x)]\n", - "master_synonyms = [x for x in ro_graph if 'synonym' in str(x[1]).lower() and isinstance(x[0], URIRef)]\n", - "\n", - "for x in tqdm(cls):\n", - " # labels\n", - " cls_label = [x for x in ro_graph.objects(x, RDFS.label) if '@' not in n3(x) or '@en' in n3(x)]\n", - " labels = str(cls_label[0]) if len(cls_label) > 0 else 'None'\n", - " # synonyms\n", - " cls_syn = [str(i[2]) for i in master_synonyms if x == i[0]]\n", - " synonym = str(cls_syn[0]) if len(cls_syn) > 0 else 'None'\n", - " # description\n", - " cls_desc = [x for x in ro_graph.objects(x, obo.IAO_0000115) if '@' not in n3(x) or '@en' in n3(x)]\n", - " desc = '|'.join([str(cls_desc[0])]) if len(cls_desc) > 0 else 'None'\n", + "master_metadata_dictionary['edges'].update({'gene-disease': {}, 'gene-phenotype': {}})\n", + "\n", + "# create dictionary\n", + "for idx, row in tqdm(dgt_dis_gene.iterrows(), total=dgt_dis_gene.shape[0]):\n", + " node_key = row['geneId']; dis_id = row['diseaseId']; dis_name = row['diseaseName']\n", + " sem_type = row['diseaseSemanticType']; dis_cls = row['diseaseClass']\n", + " evidence = [{'DisGeNET_DSI': row['DSI'],\n", + " 'DisGeNET_DPI': row['DPI'],\n", + " 'DisGeNET_score': row['score'],\n", + " 'DisGeNET_EI': row['EI'],\n", + " 'DisGeNET_YearInitial': row['YearInitial'],\n", + " 'DisGeNET_YearFinal': row['YearFinal'],\n", + " 'DisGeNET_NofPmids': row['NofPmids'],\n", + " 'DisGeNET_NofSnps': row['NofSnps']}] \n", + " if row['diseaseType'] == 'disease':\n", + " node_key2 = row['MONDO_IDs']; edge_key = '{}-{}'.format(node_key, node_key2); edge_type = 'gene-disease'\n", + " else:\n", + " node_key2 = row['HP_IDs']; edge_key = '{}-{}'.format(node_key, node_key2); edge_type = 'gene-phenotype'\n", " \n", - " relation_metadata_dict[str(x)] = {\n", - " 'Label': labels, 'Description': desc, 'Synonym': synonym\n", - " }" + " # add disease/phenotype information\n", + " if node_key2 in master_metadata_dictionary['nodes'].keys():\n", + " if url in master_metadata_dictionary['nodes'][node_key2].keys():\n", + " master_metadata_dictionary['nodes'][node_key2][url]['DisGeNET_diseaseId'] |= {dis_id}\n", + " master_metadata_dictionary['nodes'][node_key2][url]['DisGeNET_diseaseName'] |= {dis_name}\n", + " master_metadata_dictionary['nodes'][node_key2][url]['DisGeNET_diseaseSemanticType'] |= {sem_type}\n", + " master_metadata_dictionary['nodes'][node_key2][url]['DisGeNET_diseaseClass'] |= {dis_cls}\n", + " else:\n", + " master_metadata_dictionary['nodes'][node_key2].update({url: {\n", + " 'DisGeNET_diseaseId': {dis_id},\n", + " 'DisGeNET_diseaseName': {dis_name},\n", + " 'DisGeNET_diseaseSemanticType': {sem_type},\n", + " 'DisGeNET_diseaseClass': {dis_cls}}})\n", + " else:\n", + " master_metadata_dictionary['nodes'].update({node_key2: {url: {\n", + " 'DisGeNET_diseaseId': {dis_id},\n", + " 'DisGeNET_diseaseName': {dis_name},\n", + " 'DisGeNET_diseaseSemanticType': {sem_type},\n", + " 'DisGeNET_diseaseClass': {dis_cls}}}})\n", + "\n", + " # add genomic information\n", + " if node_key in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[node_key]\n", + " if node_key in master_metadata_dictionary['nodes'].keys():\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'][node_key].update({'genomic_data': genomic_info_dict})\n", + " else: master_metadata_dictionary['nodes'][node_key].update({'genomic_data': 'None'})\n", + " else:\n", + " if genomic_info_dict is not None:\n", + " master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': genomic_info_dict}})\n", + " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", + "\n", + " # add relation data to dictionary\n", + " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", + " if 'DisGeNET_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", + " inital_ev = inital_ev['DisGeNET_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_type][edge_key][url]['DisGeNET_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'DisGeNET_Evidence': evidence}\n", + " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'DisGeNET_Evidence': evidence}}\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "***\n", + "<br>\n", "\n", - "**Create Master Metadata Dictionary** \n", + "***\n", "\n", - "To make it easier to navigate the mapping of each instance node in an edge, a master dictionary is created and keyed by node type. This is most useful when both nodes in an edge are instances, but of different data types (e.g. `gene-rna`).\n" + "#### Save Metadata Dictionary\n", + "Write the metadata dictionary to a file named `entity_metadata_dict.pkl` and located in the `resources/metadata/` directory." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 161, "metadata": {}, "outputs": [], "source": [ - "# combine all metadata dictionaries\n", - "master_metadata_dictionary = {'nodes': {**gene_metadata_dict,\n", - " **rna_metadata_dict,\n", - " **var_metadata_dict,\n", - " **pathway_metadata_dict},\n", - " 'relations': relation_metadata_dict}\n", + "# save a copy of the dictionary\n", + "# output > 4GB requires special approach: https://stackoverflow.com/questions/42653386/does-pickle-randomly-fail-with-oserror-on-large-files\n", + "filepath = metadata_location + 'entity_metadata_dict.pkl'\n", "\n", - "# verify metadata strings are properly formatted\n", - "temp_copy = master_metadata_dictionary.copy(); master_metadata_dictionary = dict()\n", - "for key, value in tqdm(temp_copy.items()):\n", - " master_metadata_dictionary[key] = {}\n", - " for ent_key, ent_value in value.items():\n", - " updated_inner_dict = {k: re.sub('\\s\\s+', ' ', v.replace('\\n', ' '))\n", - " if v is not None else v for k, v in ent_value.items()}\n", - " master_metadata_dictionary[key][ent_key] = updated_inner_dict\n", - "del temp_copy\n", + "# defensive way to write pickle.write, allowing for very large files on all platforms\n", + "max_bytes, bytes_out = 2**31 - 1, pickle.dumps(master_metadata_dictionary)\n", + "n_bytes = sys.getsizeof(bytes_out)\n", "\n", - "# save dictionary locally\n", - "pickle.dump(master_metadata_dictionary, open(node_data_location + 'node_metadata_dict.pkl', 'wb'), protocol=4)" + "with open(filepath, 'wb') as f_out:\n", + " for idx in range(0, n_bytes, max_bytes):\n", + " f_out.write(bytes_out[idx:idx+max_bytes])" ] }, { From 2730a1ea65a81d3d7d5de72d2ddbc168ccb131a8 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 28 Jan 2022 11:02:09 -0500 Subject: [PATCH 081/112] moving metadata file --- .gitignore | 1 + .../metadata/pheknowlator_source_metadata.xlsx | Bin 0 -> 54315 bytes resources/pheknowlator_source_metadata.xlsx | Bin 39378 -> 0 bytes resources/~$pheknowlator_source_metadata.xlsx | Bin 0 -> 165 bytes 4 files changed, 1 insertion(+) create mode 100644 resources/metadata/pheknowlator_source_metadata.xlsx delete mode 100644 resources/pheknowlator_source_metadata.xlsx create mode 100644 resources/~$pheknowlator_source_metadata.xlsx diff --git a/.gitignore b/.gitignore index ebcd7a2d..65ba6e7d 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ scratch*.py /resources/knowledge_graphs/ /resources/kr_model/ /resources/metadata/* +!/resources/metadata/pheknowlator_source_metadata.xlsx /resources/ontologies/* /resources/owl_decoding/* /resources/processed_data/* diff --git a/resources/metadata/pheknowlator_source_metadata.xlsx b/resources/metadata/pheknowlator_source_metadata.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c608f1f4fa06fd3930f88cee851dce3a9cb55e48 GIT binary patch literal 54315 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 literal 0 HcmV?d00001 diff --git a/resources/pheknowlator_source_metadata.xlsx b/resources/pheknowlator_source_metadata.xlsx deleted file mode 100644 index f8d068144c89a34c43155fae2d07cfafc2afdfe2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39378 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 diff --git a/resources/~$pheknowlator_source_metadata.xlsx b/resources/~$pheknowlator_source_metadata.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e8ac25398c87b17fa637a496eb0a569fb308770f GIT binary patch literal 165 zcmWd%$xKU2%&S!JQgBYp$w|ye%u^r&2r+~(WHO`yVIo5wLnVU(gBK7x19>?>n8=U; L7FPg@Q3C=1EkYIp literal 0 HcmV?d00001 From 2d501485dc55e822845a9c6b127265bbdbdbfc47 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 28 Jan 2022 11:02:26 -0500 Subject: [PATCH 082/112] updating pathway to metadata file --- resources/metadata/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/metadata/README.md b/resources/metadata/README.md index 91e200d6..c18688c8 100644 --- a/resources/metadata/README.md +++ b/resources/metadata/README.md @@ -12,11 +12,12 @@ ___ A variety of <u>metadata</u> are pulled from the data sources that are used to support external edges added to enhance the core set of ontologies. For the monthly PheKnowLator builds, please see [`pheknowlator_source_metadata. -xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/pheknowlator_source_metadata.xlsx) spreadsheet. This spreadsheet has two tabs, one for nodes and one for edges. Each entity (i.e., node or relation) there are several columns, including descriptions of the metadata, the variable type, and even examples of values for each type of metadata. +xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/metadata/pheknowlator_source_metadata.xlsx) +spreadsheet. This spreadsheet has two tabs, one for nodes and one for edges. Each entity (i.e., node or relation) there are several columns, including descriptions of the metadata, the variable type, and even examples of values for each type of metadata. *Example Metadata Dictionary Output*. The code snippet below is meant to provide a snapshot of how data are organized in the metadata dictionary. As demonstrated by this example, there are three high-level keys: - `nodes`: Nodes are keyed by CURIE. Every node has a `Label`, `Description`, `Synonym`, and `Dbxref` (whenever possible). Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. - - `edges`: Edges are keyed by a label which represents the edge type (the same label that is used in `resource_info.txt` and `edge_source_list.txt` files. Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. + - `edges`: Edges are keyed by a label which represents the edge type (the same label that is used in `resource_info.txt` and `edge_source_list.txt` files). Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. - `relations`: Relations or `owl:ObjectProperty` objects are keyed by CURIE. Similar to nodes, every relation has a `Label`, `Description`, and `Synonym` (whenever possible). Metadata that are obtained from specific sources that are not ontologies are added as a nested dictionary keyed by the filename. ```python @@ -57,7 +58,9 @@ xlsx`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/phekno The knowledge graph can be built with or without the inclusion of node and relation metadata (i.e. labels, descriptions or definitions, and synonyms). If you'd like to create and use node metadata, please run the [`Data_Preparation.ipynb`](https://github.com/callahantiff/PheKnowLator/blob/master/notebooks/Data_Preparation.ipynb) -Jupyter Notebook and run the code chunks listed under the **NODE AND RELATION METADATA** section. These code chunks should be run before the knowledge graph is constructed. For more details on what these data sources are and how they are created, please see the `metatadata` [`README.md`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/node_data/README.md). +Jupyter Notebook and run the code chunks listed under the **NODE AND RELATION METADATA** section. These code chunks +should be run before the knowledge graph is constructed. For more details on what these data sources are and how +they are created, please see the `metatadata` [`README.md`](https://github.com/callahantiff/PheKnowLator/blob/master/resources/metadata/README.md). <br> From 53f7054e62e9e2f89c52a8056df613d724c70164 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 28 Jan 2022 11:05:10 -0500 Subject: [PATCH 083/112] fixing edge type errors --- resources/resource_info.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/resource_info.txt b/resources/resource_info.txt index 98b643f7..cd07bd89 100644 --- a/resources/resource_info.txt +++ b/resources/resource_info.txt @@ -35,15 +35,15 @@ chemical-phenotype|CHEBI;HP|RO_0002606|t|1;4|0:./resources/processed_data/MESH_C chemical-protein|CHEBI;PR|RO_0002434|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('protein'); chemical-rna|CHEBI;ensembl|RO_0002434|t|1;4|0:./resources/processed_data/MESH_CHEBI_MAP.txt;1:./resources/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt|10;!=;''|6;==;Homo sapiens::5;.startswith('mRNA'); disease-phenotype|MONDO;HP|RO_0002200|t|0;3|0:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None|2;!=;NOT -gene-disease|NCBIGene;MONDO|RO_0003302|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|6;!=;group +gene-disease|NCBIGene;MONDO|RO_0003302|t|0;4|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|6;==;disease gene-gene|NCBIGene;NCBIGene|RO_0002435|t|0;1|0:./resources/processed_data/UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt;1:./resources/processed_data/UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt|None|None gene-pathway|NCBIGene;reactome|RO_0000056|t|1;3|None|None|3;.startswith('REACT:R-HSA-'); -gene-phenotype|NCBIGene;HP|RO_0003302|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|6;!=;group +gene-phenotype|NCBIGene;HP|RO_0003302|t|0;4|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|6;==;phenotype gene-protein|NCBIGene;PR|RO_0002205|t|4;1|None|None|3;==;protein-coding gene-rna|NCBIGene;ensembl|RO_0002511|t|6;1|None|None|None -gobp-pathway|GO;reactome|RO_0009501|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -pathway-gocc|reactome;GO|RO_0002180|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] -pathway-gomf|reactome;GO|RO_0000085|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME');::3;not in;["NOT"] +gobp-pathway|GO;reactome|RO_0009501|t|4;5|None|None|8;==;P::12;==;taxon:9606::5;.startswith('REACTOME'); +pathway-gocc|reactome;GO|RO_0002180|t|5;4|None|None|8;==;C::12;==;taxon:9606::5;.startswith('REACTOME'); +pathway-gomf|reactome;GO|RO_0000085|t|5;4|None|None|8;==;F::12;==;taxon:9606::5;.startswith('REACTOME'); protein-anatomy|PR;UBERON|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;anatomy protein-catalyst|PR;CHEBI|RO_0002436|t|0;1|None|None|None|None protein-cell|PR;CL|RO_0001025|t|2;6|0:./resources/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at protein level::4;==;cell line @@ -56,6 +56,6 @@ protein-protein|PR;PR|RO_0002436|''|0;1|0:./resources/processed_data/STRING_PRO_ rna-anatomy|ensembl;UBERON|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;anatomy rna-cell|ensembl;CL|RO_0001025|t|1;6|0:./resources/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt;1:./resources/processed_data/HPA_GTEx_TISSUE_CELL_MAP.txt|None|3;==;Evidence at transcript level::4;==;cell line. rna-protein|ensembl;PR|RO_0002513|t|4;1|None|None|3;==;protein-coding -variant-disease|clinvar;MONDO|RO_0003302|t|9;12|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None -variant-gene|clinvar;NCBIGene|RO_0002566|t|9;3|None|None|None -variant-phenotype|clinvar;HP|RO_0003302|t|9;12|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|None \ No newline at end of file +variant-disease|clinvar;MONDO|RO_0003302|t|0:18|1:./resources/processed_data/DISEASE_MONDO_MAP.txt|None|None +variant-gene|clinvar;NCBIGene|RO_0002566|t|0;6|None|None|None +variant-phenotype|clinvar;HP|RO_0003302|t|0;18|1:./resources/processed_data/PHENOTYPE_HPO_MAP.txt|None|None \ No newline at end of file From 51df055c416ebec3b87d639af18f279ae12c6a45 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Sat, 29 Jan 2022 14:19:22 -0500 Subject: [PATCH 084/112] cleaned up file --- .../pheknowlator_source_metadata.xlsx | Bin 54315 -> 50742 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/metadata/pheknowlator_source_metadata.xlsx b/resources/metadata/pheknowlator_source_metadata.xlsx index c608f1f4fa06fd3930f88cee851dce3a9cb55e48..3ce8ab1e5bc7b2460e3fd8ca09d89f6977a8b322 100644 GIT binary patch delta 46457 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 delta 50053 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 Ang9R* From 2face46975cbe1d8234fecedf36f344e06bbef26 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Sat, 29 Jan 2022 14:19:40 -0500 Subject: [PATCH 085/112] committing nearly complete data --- notebooks/Data_Preparation.ipynb | 4792 ++---------------------------- 1 file changed, 326 insertions(+), 4466 deletions(-) diff --git a/notebooks/Data_Preparation.ipynb b/notebooks/Data_Preparation.ipynb index ec591b6d..602631ae 100644 --- a/notebooks/Data_Preparation.ipynb +++ b/notebooks/Data_Preparation.ipynb @@ -127,7 +127,7 @@ "metadata": {}, "outputs": [], "source": [ - "# # # if running a local version of pkt_kg, uncomment the code below\n", + "# # if running a local version of pkt_kg, uncomment the code below\n", "# import sys\n", "# sys.path.append('../')" ] @@ -142,6 +142,7 @@ "import datetime\n", "import glob\n", "import itertools\n", + "import json\n", "import networkx\n", "import numpy\n", "import os\n", @@ -172,7 +173,7 @@ }, { "cell_type": "code", - "execution_count": 160, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -1738,7 +1739,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 4, "metadata": { "code_folding": [] }, @@ -4068,7 +4069,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -4084,244 +4085,9 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 2266759 variant edges\n" - ] - }, - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>AlleleID</th>\n", - " <th>Type</th>\n", - " <th>VariantName</th>\n", - " <th>GeneID</th>\n", - " <th>GeneSymbol</th>\n", - " <th>HGNC_ID</th>\n", - " <th>ClinicalSignificance</th>\n", - " <th>ClinSigSimple</th>\n", - " <th>LastEvaluated</th>\n", - " <th>RS# (dbSNP)</th>\n", - " <th>...</th>\n", - " <th>ReviewStatus</th>\n", - " <th>NumberSubmitters</th>\n", - " <th>Guidelines</th>\n", - " <th>TestedInGTR</th>\n", - " <th>OtherIDs</th>\n", - " <th>SubmitterCategories</th>\n", - " <th>VariationID</th>\n", - " <th>PositionVCF</th>\n", - " <th>ReferenceAlleleVCF</th>\n", - " <th>AlternateAlleleVCF</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>15041</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>397704705</td>\n", - " <td>...</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>2</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215070,OMIM:613653.0001</td>\n", - " <td>3</td>\n", - " <td>2</td>\n", - " <td>4820844</td>\n", - " <td>GGAT</td>\n", - " <td>TGCTGTAAACTGTAACTGTAAA</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>15041</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>397704705</td>\n", - " <td>...</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>2</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215070,OMIM:613653.0001</td>\n", - " <td>3</td>\n", - " <td>2</td>\n", - " <td>4781213</td>\n", - " <td>GGAT</td>\n", - " <td>TGCTGTAAACTGTAACTGTAAA</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>15042</td>\n", - " <td>Deletion</td>\n", - " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>June 29, 2010</td>\n", - " <td>397704709</td>\n", - " <td>...</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215072,OMIM:613653.0002</td>\n", - " <td>1</td>\n", - " <td>3</td>\n", - " <td>4827360</td>\n", - " <td>GCTGCTGGACCTGCC</td>\n", - " <td>G</td>\n", - " </tr>\n", - " <tr>\n", - " <th>3</th>\n", - " <td>15042</td>\n", - " <td>Deletion</td>\n", - " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>June 29, 2010</td>\n", - " <td>397704709</td>\n", - " <td>...</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215072,OMIM:613653.0002</td>\n", - " <td>1</td>\n", - " <td>3</td>\n", - " <td>4787729</td>\n", - " <td>GCTGCTGGACCTGCC</td>\n", - " <td>G</td>\n", - " </tr>\n", - " <tr>\n", - " <th>4</th>\n", - " <td>15043</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", - " <td>9640</td>\n", - " <td>ZNF592</td>\n", - " <td>HGNC:28986</td>\n", - " <td>Uncertain significance</td>\n", - " <td>0</td>\n", - " <td>June 29, 2015</td>\n", - " <td>150829393</td>\n", - " <td>...</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O...</td>\n", - " <td>1</td>\n", - " <td>4</td>\n", - " <td>85342440</td>\n", - " <td>G</td>\n", - " <td>A</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "<p>5 rows × 34 columns</p>\n", - "</div>" - ], - "text/plain": [ - " AlleleID Type \\\n", - "0 15041 Indel \n", - "1 15041 Indel \n", - "2 15042 Deletion \n", - "3 15042 Deletion \n", - "4 15043 single nucleotide variant \n", - "\n", - " VariantName GeneID GeneSymbol \\\n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", - "1 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", - "2 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", - "3 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", - "4 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", - "\n", - " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", - "0 HGNC:22197 Pathogenic 1 NaN \n", - "1 HGNC:22197 Pathogenic 1 NaN \n", - "2 HGNC:22197 Pathogenic 1 June 29, 2010 \n", - "3 HGNC:22197 Pathogenic 1 June 29, 2010 \n", - "4 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", - "\n", - " RS# (dbSNP) ... ReviewStatus NumberSubmitters \\\n", - "0 397704705 ... criteria provided, single submitter 2 \n", - "1 397704705 ... criteria provided, single submitter 2 \n", - "2 397704709 ... no assertion criteria provided 1 \n", - "3 397704709 ... no assertion criteria provided 1 \n", - "4 150829393 ... no assertion criteria provided 1 \n", - "\n", - " Guidelines TestedInGTR OtherIDs \\\n", - "0 NaN N ClinGen:CA215070,OMIM:613653.0001 \n", - "1 NaN N ClinGen:CA215070,OMIM:613653.0001 \n", - "2 NaN N ClinGen:CA215072,OMIM:613653.0002 \n", - "3 NaN N ClinGen:CA215072,OMIM:613653.0002 \n", - "4 NaN N ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O... \n", - "\n", - " SubmitterCategories VariationID PositionVCF ReferenceAlleleVCF \\\n", - "0 3 2 4820844 GGAT \n", - "1 3 2 4781213 GGAT \n", - "2 1 3 4827360 GCTGCTGGACCTGCC \n", - "3 1 3 4787729 GCTGCTGGACCTGCC \n", - "4 1 4 85342440 G \n", - "\n", - " AlternateAlleleVCF \n", - "0 TGCTGTAAACTGTAACTGTAAA \n", - "1 TGCTGTAAACTGTAACTGTAAA \n", - "2 G \n", - "3 G \n", - "4 A \n", - "\n", - "[5 rows x 34 columns]" - ] - }, - "execution_count": 57, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# replace \"na\" and \"-\" with NaN\n", "var_summary = var_summary.replace('na', numpy.nan)\n", @@ -4360,251 +4126,9 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 1161070 edges\n" - ] - }, - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>AlleleID</th>\n", - " <th>Type</th>\n", - " <th>VariantName</th>\n", - " <th>GeneID</th>\n", - " <th>GeneSymbol</th>\n", - " <th>HGNC_ID</th>\n", - " <th>ClinicalSignificance</th>\n", - " <th>ClinSigSimple</th>\n", - " <th>LastEvaluated</th>\n", - " <th>RS# (dbSNP)</th>\n", - " <th>...</th>\n", - " <th>Origin</th>\n", - " <th>OriginSimple</th>\n", - " <th>ReviewStatus</th>\n", - " <th>NumberSubmitters</th>\n", - " <th>Guidelines</th>\n", - " <th>TestedInGTR</th>\n", - " <th>OtherIDs</th>\n", - " <th>SubmitterCategories</th>\n", - " <th>VariationID</th>\n", - " <th>Assembly</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>15041</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>397704705</td>\n", - " <td>...</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>2</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215070,OMIM:613653.0001</td>\n", - " <td>3</td>\n", - " <td>2</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>15042</td>\n", - " <td>Deletion</td>\n", - " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>June 29, 2010</td>\n", - " <td>397704709</td>\n", - " <td>...</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215072,OMIM:613653.0002</td>\n", - " <td>1</td>\n", - " <td>3</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>4</th>\n", - " <td>15043</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", - " <td>9640</td>\n", - " <td>ZNF592</td>\n", - " <td>HGNC:28986</td>\n", - " <td>Uncertain significance</td>\n", - " <td>0</td>\n", - " <td>June 29, 2015</td>\n", - " <td>150829393</td>\n", - " <td>...</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O...</td>\n", - " <td>1</td>\n", - " <td>4</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>6</th>\n", - " <td>15044</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>HGNC:26927</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>December 30, 2019</td>\n", - " <td>267606829</td>\n", - " <td>...</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>criteria provided, multiple submitters, no con...</td>\n", - " <td>3</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA113792,OMIM:613622.0001</td>\n", - " <td>3</td>\n", - " <td>5</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>8</th>\n", - " <td>15045</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>HGNC:26927</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>October 01, 2010</td>\n", - " <td>267606830</td>\n", - " <td>...</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>UniProtKB:Q96CU9#VAR_064571,OMIM:613622.0002,C...</td>\n", - " <td>1</td>\n", - " <td>6</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "<p>5 rows × 24 columns</p>\n", - "</div>" - ], - "text/plain": [ - " AlleleID Type \\\n", - "0 15041 Indel \n", - "2 15042 Deletion \n", - "4 15043 single nucleotide variant \n", - "6 15044 single nucleotide variant \n", - "8 15045 single nucleotide variant \n", - "\n", - " VariantName GeneID GeneSymbol \\\n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", - "2 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", - "4 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", - "6 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", - "8 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", - "\n", - " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", - "0 HGNC:22197 Pathogenic 1 NaN \n", - "2 HGNC:22197 Pathogenic 1 June 29, 2010 \n", - "4 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", - "6 HGNC:26927 Pathogenic 1 December 30, 2019 \n", - "8 HGNC:26927 Pathogenic 1 October 01, 2010 \n", - "\n", - " RS# (dbSNP) ... Origin OriginSimple \\\n", - "0 397704705 ... germline;unknown germline \n", - "2 397704709 ... germline germline \n", - "4 150829393 ... germline germline \n", - "6 267606829 ... germline germline \n", - "8 267606830 ... germline germline \n", - "\n", - " ReviewStatus NumberSubmitters \\\n", - "0 criteria provided, single submitter 2 \n", - "2 no assertion criteria provided 1 \n", - "4 no assertion criteria provided 1 \n", - "6 criteria provided, multiple submitters, no con... 3 \n", - "8 no assertion criteria provided 1 \n", - "\n", - " Guidelines TestedInGTR OtherIDs \\\n", - "0 NaN N ClinGen:CA215070,OMIM:613653.0001 \n", - "2 NaN N ClinGen:CA215072,OMIM:613653.0002 \n", - "4 NaN N ClinGen:CA210674,UniProtKB:Q92610#VAR_064583,O... \n", - "6 NaN N ClinGen:CA113792,OMIM:613622.0001 \n", - "8 NaN N UniProtKB:Q96CU9#VAR_064571,OMIM:613622.0002,C... \n", - "\n", - " SubmitterCategories VariationID \\\n", - "0 3 2 \n", - "2 1 3 \n", - "4 1 4 \n", - "6 3 5 \n", - "8 1 6 \n", - "\n", - " Assembly \n", - "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "4 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "6 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "8 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "\n", - "[5 rows x 24 columns]" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# subset df\n", "var_summary_update_assemb = var_summary.copy()\n", @@ -4645,260 +4169,11 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": null, "metadata": { "code_folding": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 1161070 edges\n" - ] - }, - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>AlleleID</th>\n", - " <th>Type</th>\n", - " <th>VariantName</th>\n", - " <th>GeneID</th>\n", - " <th>GeneSymbol</th>\n", - " <th>HGNC_ID</th>\n", - " <th>ClinicalSignificance</th>\n", - " <th>ClinSigSimple</th>\n", - " <th>LastEvaluated</th>\n", - " <th>RS# (dbSNP)</th>\n", - " <th>...</th>\n", - " <th>OriginSimple</th>\n", - " <th>ReviewStatus</th>\n", - " <th>NumberSubmitters</th>\n", - " <th>Guidelines</th>\n", - " <th>TestedInGTR</th>\n", - " <th>OtherIDs</th>\n", - " <th>SubmitterCategories</th>\n", - " <th>VariationID</th>\n", - " <th>Assembly</th>\n", - " <th>Phenotype</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>15041</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>397704705</td>\n", - " <td>...</td>\n", - " <td>germline</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>2</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", - " <td>3</td>\n", - " <td>2</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>15042</td>\n", - " <td>Deletion</td>\n", - " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>June 29, 2010</td>\n", - " <td>397704709</td>\n", - " <td>...</td>\n", - " <td>germline</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", - " <td>1</td>\n", - " <td>3</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>4</th>\n", - " <td>15043</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", - " <td>9640</td>\n", - " <td>ZNF592</td>\n", - " <td>HGNC:28986</td>\n", - " <td>Uncertain significance</td>\n", - " <td>0</td>\n", - " <td>June 29, 2015</td>\n", - " <td>150829393</td>\n", - " <td>...</td>\n", - " <td>germline</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", - " <td>1</td>\n", - " <td>4</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>6</th>\n", - " <td>15044</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>HGNC:26927</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>December 30, 2019</td>\n", - " <td>267606829</td>\n", - " <td>...</td>\n", - " <td>germline</td>\n", - " <td>criteria provided, multiple submitters, no con...</td>\n", - " <td>3</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA113792|OMIM:613622.0001</td>\n", - " <td>3</td>\n", - " <td>5</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>8</th>\n", - " <td>15045</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>HGNC:26927</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>October 01, 2010</td>\n", - " <td>267606830</td>\n", - " <td>...</td>\n", - " <td>germline</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C...</td>\n", - " <td>1</td>\n", - " <td>6</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MedGen:C4748791;MONDO:0032624;OMIM:618241</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "<p>5 rows × 23 columns</p>\n", - "</div>" - ], - "text/plain": [ - " AlleleID Type \\\n", - "0 15041 Indel \n", - "2 15042 Deletion \n", - "4 15043 single nucleotide variant \n", - "6 15044 single nucleotide variant \n", - "8 15045 single nucleotide variant \n", - "\n", - " VariantName GeneID GeneSymbol \\\n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", - "2 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", - "4 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", - "6 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", - "8 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", - "\n", - " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", - "0 HGNC:22197 Pathogenic 1 NaN \n", - "2 HGNC:22197 Pathogenic 1 June 29, 2010 \n", - "4 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", - "6 HGNC:26927 Pathogenic 1 December 30, 2019 \n", - "8 HGNC:26927 Pathogenic 1 October 01, 2010 \n", - "\n", - " RS# (dbSNP) ... OriginSimple \\\n", - "0 397704705 ... germline \n", - "2 397704709 ... germline \n", - "4 150829393 ... germline \n", - "6 267606829 ... germline \n", - "8 267606830 ... germline \n", - "\n", - " ReviewStatus NumberSubmitters \\\n", - "0 criteria provided, single submitter 2 \n", - "2 no assertion criteria provided 1 \n", - "4 no assertion criteria provided 1 \n", - "6 criteria provided, multiple submitters, no con... 3 \n", - "8 no assertion criteria provided 1 \n", - "\n", - " Guidelines TestedInGTR OtherIDs \\\n", - "0 NaN N ClinGen:CA215070|OMIM:613653.0001 \n", - "2 NaN N ClinGen:CA215072|OMIM:613653.0002 \n", - "4 NaN N ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... \n", - "6 NaN N ClinGen:CA113792|OMIM:613622.0001 \n", - "8 NaN N UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C... \n", - "\n", - " SubmitterCategories VariationID \\\n", - "0 3 2 \n", - "2 1 3 \n", - "4 1 4 \n", - "6 3 5 \n", - "8 1 6 \n", - "\n", - " Assembly \\\n", - "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "4 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "6 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "8 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "\n", - " Phenotype \n", - "0 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", - "2 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", - "4 MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM... \n", - "6 MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00... \n", - "8 MedGen:C4748791;MONDO:0032624;OMIM:618241 \n", - "\n", - "[5 rows x 23 columns]" - ] - }, - "execution_count": 59, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# clean-up identifiers\n", "var_summary_update['Phenotype'] = var_summary_update['PhenotypeIDS'].str.replace('|', ';').str.replace(',', ';')\n", @@ -4967,7 +4242,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -4983,85 +4258,9 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 762226 edges\n" - ] - }, - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>VariationID</th>\n", - " <th>Citation</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>2</td>\n", - " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>3</td>\n", - " <td>PubMed:20613862</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>4</td>\n", - " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", - " </tr>\n", - " <tr>\n", - " <th>3</th>\n", - " <td>5</td>\n", - " <td>PubMed:30723688|PubMed:25678554|PubMed:20818383</td>\n", - " </tr>\n", - " <tr>\n", - " <th>4</th>\n", - " <td>6</td>\n", - " <td>PubMed:20818383</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " VariationID Citation\n", - "0 2 PubMed:20613862|PubMed:25741868|PubMedCentral:...\n", - "1 3 PubMed:20613862\n", - "2 4 PubMed:26123727|PubMed:12030328|PubMed:20531441\n", - "3 5 PubMed:30723688|PubMed:25678554|PubMed:20818383\n", - "4 6 PubMed:20818383" - ] - }, - "execution_count": 61, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# replace \"na\" and \"-\" with NaN\n", "var_citations = var_citations.replace('na', numpy.nan)\n", @@ -5109,7 +4308,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -5125,129 +4324,9 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 2328173 edges\n" - ] - }, - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>AlleleID</th>\n", - " <th>GeneID</th>\n", - " <th>GeneSymbol</th>\n", - " <th>GeneName</th>\n", - " <th>GenesPerAlleleID</th>\n", - " <th>Category</th>\n", - " <th>Source</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>15041</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>15042</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>15043</td>\n", - " <td>9640</td>\n", - " <td>ZNF592</td>\n", - " <td>zinc finger protein 592</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " <tr>\n", - " <th>3</th>\n", - " <td>15044</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>FAD dependent oxidoreductase domain containing 1</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " <tr>\n", - " <th>4</th>\n", - " <td>15045</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>FAD dependent oxidoreductase domain containing 1</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " AlleleID GeneID GeneSymbol \\\n", - "0 15041 9907 AP5Z1 \n", - "1 15042 9907 AP5Z1 \n", - "2 15043 9640 ZNF592 \n", - "3 15044 55572 FOXRED1 \n", - "4 15045 55572 FOXRED1 \n", - "\n", - " GeneName GenesPerAlleleID \\\n", - "0 adaptor related protein complex 5 subunit zeta 1 1 \n", - "1 adaptor related protein complex 5 subunit zeta 1 1 \n", - "2 zinc finger protein 592 1 \n", - "3 FAD dependent oxidoreductase domain containing 1 1 \n", - "4 FAD dependent oxidoreductase domain containing 1 1 \n", - "\n", - " Category Source \n", - "0 within single gene submitted \n", - "1 within single gene submitted \n", - "2 within single gene submitted \n", - "3 within single gene submitted \n", - "4 within single gene submitted " - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# replace \"na\" and \"-\" with NaN\n", "allele_gene = allele_gene.replace('na', numpy.nan)\n", @@ -5285,258 +4364,9 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 1161070 edges\n" - ] - }, - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>AlleleID</th>\n", - " <th>Type</th>\n", - " <th>VariantName</th>\n", - " <th>GeneID</th>\n", - " <th>GeneSymbol</th>\n", - " <th>HGNC_ID</th>\n", - " <th>ClinicalSignificance</th>\n", - " <th>ClinSigSimple</th>\n", - " <th>LastEvaluated</th>\n", - " <th>RS# (dbSNP)</th>\n", - " <th>...</th>\n", - " <th>ReviewStatus</th>\n", - " <th>NumberSubmitters</th>\n", - " <th>Guidelines</th>\n", - " <th>TestedInGTR</th>\n", - " <th>OtherIDs</th>\n", - " <th>SubmitterCategories</th>\n", - " <th>VariationID</th>\n", - " <th>Assembly</th>\n", - " <th>Phenotype</th>\n", - " <th>Citation</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>15041</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>397704705</td>\n", - " <td>...</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>2</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", - " <td>3</td>\n", - " <td>2</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", - " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>15042</td>\n", - " <td>Deletion</td>\n", - " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>June 29, 2010</td>\n", - " <td>397704709</td>\n", - " <td>...</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", - " <td>1</td>\n", - " <td>3</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", - " <td>PubMed:20613862</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>15043</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", - " <td>9640</td>\n", - " <td>ZNF592</td>\n", - " <td>HGNC:28986</td>\n", - " <td>Uncertain significance</td>\n", - " <td>0</td>\n", - " <td>June 29, 2015</td>\n", - " <td>150829393</td>\n", - " <td>...</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", - " <td>1</td>\n", - " <td>4</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM...</td>\n", - " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", - " </tr>\n", - " <tr>\n", - " <th>3</th>\n", - " <td>15044</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>HGNC:26927</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>December 30, 2019</td>\n", - " <td>267606829</td>\n", - " <td>...</td>\n", - " <td>criteria provided, multiple submitters, no con...</td>\n", - " <td>3</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>ClinGen:CA113792|OMIM:613622.0001</td>\n", - " <td>3</td>\n", - " <td>5</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00...</td>\n", - " <td>PubMed:30723688|PubMed:25678554|PubMed:20818383</td>\n", - " </tr>\n", - " <tr>\n", - " <th>4</th>\n", - " <td>15045</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>HGNC:26927</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>October 01, 2010</td>\n", - " <td>267606830</td>\n", - " <td>...</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C...</td>\n", - " <td>1</td>\n", - " <td>6</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MedGen:C4748791;MONDO:0032624;OMIM:618241</td>\n", - " <td>PubMed:20818383</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "<p>5 rows × 24 columns</p>\n", - "</div>" - ], - "text/plain": [ - " AlleleID Type \\\n", - "0 15041 Indel \n", - "1 15042 Deletion \n", - "2 15043 single nucleotide variant \n", - "3 15044 single nucleotide variant \n", - "4 15045 single nucleotide variant \n", - "\n", - " VariantName GeneID GeneSymbol \\\n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", - "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", - "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", - "3 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", - "4 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", - "\n", - " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", - "0 HGNC:22197 Pathogenic 1 NaN \n", - "1 HGNC:22197 Pathogenic 1 June 29, 2010 \n", - "2 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", - "3 HGNC:26927 Pathogenic 1 December 30, 2019 \n", - "4 HGNC:26927 Pathogenic 1 October 01, 2010 \n", - "\n", - " RS# (dbSNP) ... ReviewStatus \\\n", - "0 397704705 ... criteria provided, single submitter \n", - "1 397704709 ... no assertion criteria provided \n", - "2 150829393 ... no assertion criteria provided \n", - "3 267606829 ... criteria provided, multiple submitters, no con... \n", - "4 267606830 ... no assertion criteria provided \n", - "\n", - " NumberSubmitters Guidelines TestedInGTR \\\n", - "0 2 NaN N \n", - "1 1 NaN N \n", - "2 1 NaN N \n", - "3 3 NaN N \n", - "4 1 NaN N \n", - "\n", - " OtherIDs SubmitterCategories \\\n", - "0 ClinGen:CA215070|OMIM:613653.0001 3 \n", - "1 ClinGen:CA215072|OMIM:613653.0002 1 \n", - "2 ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... 1 \n", - "3 ClinGen:CA113792|OMIM:613622.0001 3 \n", - "4 UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C... 1 \n", - "\n", - " VariationID Assembly \\\n", - "0 2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "1 3 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "2 4 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "3 5 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "4 6 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "\n", - " Phenotype \\\n", - "0 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", - "1 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", - "2 MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM... \n", - "3 MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00... \n", - "4 MedGen:C4748791;MONDO:0032624;OMIM:618241 \n", - "\n", - " Citation \n", - "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", - "1 PubMed:20613862 \n", - "2 PubMed:26123727|PubMed:12030328|PubMed:20531441 \n", - "3 PubMed:30723688|PubMed:25678554|PubMed:20818383 \n", - "4 PubMed:20818383 \n", - "\n", - "[5 rows x 24 columns]" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# merge data\n", "merge_cols = list(set(var_summary_update.columns).intersection(set(var_citations.columns)))\n", @@ -5556,265 +4386,9 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 1161070 edges\n" - ] - }, - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>AlleleID</th>\n", - " <th>Type</th>\n", - " <th>VariantName</th>\n", - " <th>GeneID</th>\n", - " <th>GeneSymbol</th>\n", - " <th>HGNC_ID</th>\n", - " <th>ClinicalSignificance</th>\n", - " <th>ClinSigSimple</th>\n", - " <th>LastEvaluated</th>\n", - " <th>RS# (dbSNP)</th>\n", - " <th>...</th>\n", - " <th>OtherIDs</th>\n", - " <th>SubmitterCategories</th>\n", - " <th>VariationID</th>\n", - " <th>Assembly</th>\n", - " <th>Phenotype</th>\n", - " <th>Citation</th>\n", - " <th>GeneName</th>\n", - " <th>GenesPerAlleleID</th>\n", - " <th>Category</th>\n", - " <th>Source</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>15041</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>397704705</td>\n", - " <td>...</td>\n", - " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", - " <td>3</td>\n", - " <td>2</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", - " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", - " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>15042</td>\n", - " <td>Deletion</td>\n", - " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", - " <td>9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>HGNC:22197</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>June 29, 2010</td>\n", - " <td>397704709</td>\n", - " <td>...</td>\n", - " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", - " <td>1</td>\n", - " <td>3</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:...</td>\n", - " <td>PubMed:20613862</td>\n", - " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>15043</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", - " <td>9640</td>\n", - " <td>ZNF592</td>\n", - " <td>HGNC:28986</td>\n", - " <td>Uncertain significance</td>\n", - " <td>0</td>\n", - " <td>June 29, 2015</td>\n", - " <td>150829393</td>\n", - " <td>...</td>\n", - " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", - " <td>1</td>\n", - " <td>4</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM...</td>\n", - " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", - " <td>zinc finger protein 592</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " <tr>\n", - " <th>3</th>\n", - " <td>15044</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>HGNC:26927</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>December 30, 2019</td>\n", - " <td>267606829</td>\n", - " <td>...</td>\n", - " <td>ClinGen:CA113792|OMIM:613622.0001</td>\n", - " <td>3</td>\n", - " <td>5</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00...</td>\n", - " <td>PubMed:30723688|PubMed:25678554|PubMed:20818383</td>\n", - " <td>FAD dependent oxidoreductase domain containing 1</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " <tr>\n", - " <th>4</th>\n", - " <td>15045</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", - " <td>55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>HGNC:26927</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>October 01, 2010</td>\n", - " <td>267606830</td>\n", - " <td>...</td>\n", - " <td>UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C...</td>\n", - " <td>1</td>\n", - " <td>6</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MedGen:C4748791;MONDO:0032624;OMIM:618241</td>\n", - " <td>PubMed:20818383</td>\n", - " <td>FAD dependent oxidoreductase domain containing 1</td>\n", - " <td>1</td>\n", - " <td>within single gene</td>\n", - " <td>submitted</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "<p>5 rows × 28 columns</p>\n", - "</div>" - ], - "text/plain": [ - " AlleleID Type \\\n", - "0 15041 Indel \n", - "1 15042 Deletion \n", - "2 15043 single nucleotide variant \n", - "3 15044 single nucleotide variant \n", - "4 15045 single nucleotide variant \n", - "\n", - " VariantName GeneID GeneSymbol \\\n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... 9907 AP5Z1 \n", - "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) 9907 AP5Z1 \n", - "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) 9640 ZNF592 \n", - "3 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) 55572 FOXRED1 \n", - "4 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) 55572 FOXRED1 \n", - "\n", - " HGNC_ID ClinicalSignificance ClinSigSimple LastEvaluated \\\n", - "0 HGNC:22197 Pathogenic 1 NaN \n", - "1 HGNC:22197 Pathogenic 1 June 29, 2010 \n", - "2 HGNC:28986 Uncertain significance 0 June 29, 2015 \n", - "3 HGNC:26927 Pathogenic 1 December 30, 2019 \n", - "4 HGNC:26927 Pathogenic 1 October 01, 2010 \n", - "\n", - " RS# (dbSNP) ... OtherIDs \\\n", - "0 397704705 ... ClinGen:CA215070|OMIM:613653.0001 \n", - "1 397704709 ... ClinGen:CA215072|OMIM:613653.0002 \n", - "2 150829393 ... ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... \n", - "3 267606829 ... ClinGen:CA113792|OMIM:613622.0001 \n", - "4 267606830 ... UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C... \n", - "\n", - " SubmitterCategories VariationID \\\n", - "0 3 2 \n", - "1 1 3 \n", - "2 1 4 \n", - "3 3 5 \n", - "4 1 6 \n", - "\n", - " Assembly \\\n", - "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "1 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "3 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "4 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... \n", - "\n", - " Phenotype \\\n", - "0 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", - "1 MONDO:0013342;OMIM:613647;ORPHA:306511;MedGen:... \n", - "2 MedGen:C4551772;MONDO:0033005;ORPHA:83472;OMIM... \n", - "3 MONDO:0032624;OMIM:256000;OMIM:618241;MONDO:00... \n", - "4 MedGen:C4748791;MONDO:0032624;OMIM:618241 \n", - "\n", - " Citation \\\n", - "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", - "1 PubMed:20613862 \n", - "2 PubMed:26123727|PubMed:12030328|PubMed:20531441 \n", - "3 PubMed:30723688|PubMed:25678554|PubMed:20818383 \n", - "4 PubMed:20818383 \n", - "\n", - " GeneName GenesPerAlleleID \\\n", - "0 adaptor related protein complex 5 subunit zeta 1 1 \n", - "1 adaptor related protein complex 5 subunit zeta 1 1 \n", - "2 zinc finger protein 592 1 \n", - "3 FAD dependent oxidoreductase domain containing 1 1 \n", - "4 FAD dependent oxidoreductase domain containing 1 1 \n", - "\n", - " Category Source \n", - "0 within single gene submitted \n", - "1 within single gene submitted \n", - "2 within single gene submitted \n", - "3 within single gene submitted \n", - "4 within single gene submitted \n", - "\n", - "[5 rows x 28 columns]" - ] - }, - "execution_count": 65, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# merge data\n", "merge_cols = list(set(var_summary_merged.columns).intersection(set(allele_gene.columns)))\n", @@ -5844,251 +4418,9 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 1161070 edges\n" - ] - }, - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>VariationID</th>\n", - " <th>AlleleID</th>\n", - " <th>RS# (dbSNP)</th>\n", - " <th>Type</th>\n", - " <th>VariantName</th>\n", - " <th>OtherIDs</th>\n", - " <th>GeneID</th>\n", - " <th>GeneSymbol</th>\n", - " <th>GeneName</th>\n", - " <th>GenesPerAlleleID</th>\n", - " <th>...</th>\n", - " <th>LastEvaluated</th>\n", - " <th>ReviewStatus</th>\n", - " <th>ClinicalSignificance</th>\n", - " <th>ClinSigSimple</th>\n", - " <th>Origin</th>\n", - " <th>OriginSimple</th>\n", - " <th>Source</th>\n", - " <th>SubmitterCategories</th>\n", - " <th>NumberSubmitters</th>\n", - " <th>Citation</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>clinvar_2</td>\n", - " <td>15041</td>\n", - " <td>397704705</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", - " <td>NCBIGene_9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", - " <td>1</td>\n", - " <td>...</td>\n", - " <td>NaN</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>submitted</td>\n", - " <td>3</td>\n", - " <td>2</td>\n", - " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>clinvar_3</td>\n", - " <td>15042</td>\n", - " <td>397704709</td>\n", - " <td>Deletion</td>\n", - " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", - " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", - " <td>NCBIGene_9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", - " <td>1</td>\n", - " <td>...</td>\n", - " <td>June 29, 2010</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>submitted</td>\n", - " <td>1</td>\n", - " <td>1</td>\n", - " <td>PubMed:20613862</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>clinvar_4</td>\n", - " <td>15043</td>\n", - " <td>150829393</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", - " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", - " <td>NCBIGene_9640</td>\n", - " <td>ZNF592</td>\n", - " <td>zinc finger protein 592</td>\n", - " <td>1</td>\n", - " <td>...</td>\n", - " <td>June 29, 2015</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>Uncertain significance</td>\n", - " <td>0</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>submitted</td>\n", - " <td>1</td>\n", - " <td>1</td>\n", - " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", - " </tr>\n", - " <tr>\n", - " <th>3</th>\n", - " <td>clinvar_5</td>\n", - " <td>15044</td>\n", - " <td>267606829</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.694C&gt;T (p.Gln232Ter)</td>\n", - " <td>ClinGen:CA113792|OMIM:613622.0001</td>\n", - " <td>NCBIGene_55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>FAD dependent oxidoreductase domain containing 1</td>\n", - " <td>1</td>\n", - " <td>...</td>\n", - " <td>December 30, 2019</td>\n", - " <td>criteria provided, multiple submitters, no con...</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>submitted</td>\n", - " <td>3</td>\n", - " <td>3</td>\n", - " <td>PubMed:30723688|PubMed:25678554|PubMed:20818383</td>\n", - " </tr>\n", - " <tr>\n", - " <th>4</th>\n", - " <td>clinvar_6</td>\n", - " <td>15045</td>\n", - " <td>267606830</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_017547.4(FOXRED1):c.1289A&gt;G (p.Asn430Ser)</td>\n", - " <td>UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C...</td>\n", - " <td>NCBIGene_55572</td>\n", - " <td>FOXRED1</td>\n", - " <td>FAD dependent oxidoreductase domain containing 1</td>\n", - " <td>1</td>\n", - " <td>...</td>\n", - " <td>October 01, 2010</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>submitted</td>\n", - " <td>1</td>\n", - " <td>1</td>\n", - " <td>PubMed:20818383</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "<p>5 rows × 25 columns</p>\n", - "</div>" - ], - "text/plain": [ - " VariationID AlleleID RS# (dbSNP) Type \\\n", - "0 clinvar_2 15041 397704705 Indel \n", - "1 clinvar_3 15042 397704709 Deletion \n", - "2 clinvar_4 15043 150829393 single nucleotide variant \n", - "3 clinvar_5 15044 267606829 single nucleotide variant \n", - "4 clinvar_6 15045 267606830 single nucleotide variant \n", - "\n", - " VariantName \\\n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... \n", - "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) \n", - "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) \n", - "3 NM_017547.4(FOXRED1):c.694C>T (p.Gln232Ter) \n", - "4 NM_017547.4(FOXRED1):c.1289A>G (p.Asn430Ser) \n", - "\n", - " OtherIDs GeneID \\\n", - "0 ClinGen:CA215070|OMIM:613653.0001 NCBIGene_9907 \n", - "1 ClinGen:CA215072|OMIM:613653.0002 NCBIGene_9907 \n", - "2 ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... NCBIGene_9640 \n", - "3 ClinGen:CA113792|OMIM:613622.0001 NCBIGene_55572 \n", - "4 UniProtKB:Q96CU9#VAR_064571|OMIM:613622.0002|C... NCBIGene_55572 \n", - "\n", - " GeneSymbol GeneName \\\n", - "0 AP5Z1 adaptor related protein complex 5 subunit zeta 1 \n", - "1 AP5Z1 adaptor related protein complex 5 subunit zeta 1 \n", - "2 ZNF592 zinc finger protein 592 \n", - "3 FOXRED1 FAD dependent oxidoreductase domain containing 1 \n", - "4 FOXRED1 FAD dependent oxidoreductase domain containing 1 \n", - "\n", - " GenesPerAlleleID ... LastEvaluated \\\n", - "0 1 ... NaN \n", - "1 1 ... June 29, 2010 \n", - "2 1 ... June 29, 2015 \n", - "3 1 ... December 30, 2019 \n", - "4 1 ... October 01, 2010 \n", - "\n", - " ReviewStatus ClinicalSignificance \\\n", - "0 criteria provided, single submitter Pathogenic \n", - "1 no assertion criteria provided Pathogenic \n", - "2 no assertion criteria provided Uncertain significance \n", - "3 criteria provided, multiple submitters, no con... Pathogenic \n", - "4 no assertion criteria provided Pathogenic \n", - "\n", - " ClinSigSimple Origin OriginSimple Source SubmitterCategories \\\n", - "0 1 germline;unknown germline submitted 3 \n", - "1 1 germline germline submitted 1 \n", - "2 0 germline germline submitted 1 \n", - "3 1 germline germline submitted 3 \n", - "4 1 germline germline submitted 1 \n", - "\n", - " NumberSubmitters Citation \n", - "0 2 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", - "1 1 PubMed:20613862 \n", - "2 1 PubMed:26123727|PubMed:12030328|PubMed:20531441 \n", - "3 3 PubMed:30723688|PubMed:25678554|PubMed:20818383 \n", - "4 1 PubMed:20818383 \n", - "\n", - "[5 rows x 25 columns]" - ] - }, - "execution_count": 66, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# reduce data set\n", "var_summary_merged_gene = var_summary_merged.copy()\n", @@ -6100,6 +4432,9 @@ " 'SubmitterCategories', 'NumberSubmitters', 'Citation']]\n", "var_summary_merged_gene.drop_duplicates(inplace=True)\n", "\n", + "# remove any rows missing a gene id\n", + "var_summary_merged_gene = var_summary_merged_gene.dropna(subset=['GeneID'])\n", + "\n", "# head prefix to output\n", "var_summary_merged_gene['GeneID'] = 'NCBIGene_' + var_summary_merged_gene['GeneID'].astype(str)\n", "var_summary_merged_gene['VariationID'] = 'clinvar_' + var_summary_merged_gene['VariationID'].astype(str)\n", @@ -6111,7 +4446,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -6129,255 +4464,15 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 1/1 [00:57<00:00, 57.46s/it]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 4769048 edges\n" - ] - }, - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>VariationID</th>\n", - " <th>AlleleID</th>\n", - " <th>RS# (dbSNP)</th>\n", - " <th>Type</th>\n", - " <th>VariantName</th>\n", - " <th>RCVaccession</th>\n", - " <th>LastEvaluated</th>\n", - " <th>ReviewStatus</th>\n", - " <th>ClinicalSignificance</th>\n", - " <th>ClinSigSimple</th>\n", - " <th>NumberSubmitters</th>\n", - " <th>SubmitterCategories</th>\n", - " <th>Guidelines</th>\n", - " <th>TestedInGTR</th>\n", - " <th>Origin</th>\n", - " <th>OriginSimple</th>\n", - " <th>Assembly</th>\n", - " <th>Phenotype</th>\n", - " <th>Citation</th>\n", - " <th>OtherIDs</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>clinvar_2</td>\n", - " <td>15041</td>\n", - " <td>397704705</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>RCV000000012</td>\n", - " <td>NaN</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>2</td>\n", - " <td>3</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0013342</td>\n", - " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", - " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", - " </tr>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>clinvar_2</td>\n", - " <td>15041</td>\n", - " <td>397704705</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>RCV000000012</td>\n", - " <td>NaN</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>2</td>\n", - " <td>3</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>OMIM:613647</td>\n", - " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", - " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", - " </tr>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>clinvar_2</td>\n", - " <td>15041</td>\n", - " <td>397704705</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>RCV000000012</td>\n", - " <td>NaN</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>2</td>\n", - " <td>3</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>ORPHA:306511</td>\n", - " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", - " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", - " </tr>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>clinvar_2</td>\n", - " <td>15041</td>\n", - " <td>397704705</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>RCV000000012</td>\n", - " <td>NaN</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>2</td>\n", - " <td>3</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MedGen:C3150901</td>\n", - " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", - " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>clinvar_3</td>\n", - " <td>15042</td>\n", - " <td>397704709</td>\n", - " <td>Deletion</td>\n", - " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", - " <td>RCV000000013</td>\n", - " <td>June 29, 2010</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>1</td>\n", - " <td>1</td>\n", - " <td>NaN</td>\n", - " <td>N</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0013342</td>\n", - " <td>PubMed:20613862</td>\n", - " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " VariationID AlleleID RS# (dbSNP) Type \\\n", - "0 clinvar_2 15041 397704705 Indel \n", - "0 clinvar_2 15041 397704705 Indel \n", - "0 clinvar_2 15041 397704705 Indel \n", - "0 clinvar_2 15041 397704705 Indel \n", - "1 clinvar_3 15042 397704709 Deletion \n", - "\n", - " VariantName RCVaccession \\\n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... RCV000000012 \n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... RCV000000012 \n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... RCV000000012 \n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... RCV000000012 \n", - "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) RCV000000013 \n", - "\n", - " LastEvaluated ReviewStatus ClinicalSignificance \\\n", - "0 NaN criteria provided, single submitter Pathogenic \n", - "0 NaN criteria provided, single submitter Pathogenic \n", - "0 NaN criteria provided, single submitter Pathogenic \n", - "0 NaN criteria provided, single submitter Pathogenic \n", - "1 June 29, 2010 no assertion criteria provided Pathogenic \n", - "\n", - " ClinSigSimple NumberSubmitters SubmitterCategories Guidelines \\\n", - "0 1 2 3 NaN \n", - "0 1 2 3 NaN \n", - "0 1 2 3 NaN \n", - "0 1 2 3 NaN \n", - "1 1 1 1 NaN \n", - "\n", - " TestedInGTR Origin OriginSimple \\\n", - "0 N germline;unknown germline \n", - "0 N germline;unknown germline \n", - "0 N germline;unknown germline \n", - "0 N germline;unknown germline \n", - "1 N germline germline \n", - "\n", - " Assembly Phenotype \\\n", - "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0013342 \n", - "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... OMIM:613647 \n", - "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... ORPHA:306511 \n", - "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MedGen:C3150901 \n", - "1 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0013342 \n", - "\n", - " Citation \\\n", - "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", - "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", - "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", - "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", - "1 PubMed:20613862 \n", - "\n", - " OtherIDs \n", - "0 ClinGen:CA215070|OMIM:613653.0001 \n", - "0 ClinGen:CA215070|OMIM:613653.0001 \n", - "0 ClinGen:CA215070|OMIM:613653.0001 \n", - "0 ClinGen:CA215070|OMIM:613653.0001 \n", - "1 ClinGen:CA215072|OMIM:613653.0002 " - ] - }, - "execution_count": 68, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# reduce data set\n", "var_summary_merged_disease = var_summary_merged.copy()\n", "var_summary_merged_disease = var_summary_merged_disease[[\n", " 'VariationID', 'AlleleID', 'RS# (dbSNP)', 'Type', 'VariantName', 'RCVaccession',\n", - " 'LastEvaluated', 'ReviewStatus', 'ClinicalSignificance', 'ClinSigSimple',\n", + " 'LastEvaluated', 'ReviewStatus', 'ClinicalSignificance', 'ClinSigSimple', 'GeneID',\n", " 'NumberSubmitters', 'SubmitterCategories', 'Guidelines', 'TestedInGTR',\n", " 'Origin', 'OriginSimple', 'Assembly', 'Phenotype', 'Citation', 'OtherIDs']]\n", "var_summary_merged_disease.drop_duplicates(inplace=True)\n", @@ -6400,7 +4495,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -6679,7 +4774,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -6720,7 +4815,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -6737,14 +4832,14 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 65015/65015 [00:20<00:00, 3173.30it/s]\n" + "100%|██████████| 65015/65015 [00:20<00:00, 3222.04it/s]\n" ] } ], @@ -6773,7 +4868,10 @@ " 'Label': ''.join(lab),\n", " 'Description': ''.join(desc),\n", " 'Synonym': '|'.join(syn),\n", - " 'Dbxref': dbxref}" + " 'Dbxref': dbxref}\n", + "\n", + "# delete unneeded data\n", + "del entrez_gene_data" ] }, { @@ -6787,7 +4885,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -6810,14 +4908,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 248494/248494 [00:45<00:00, 5411.57it/s]\n" + " 24%|██▎ | 58528/248494 [00:11<00:36, 5141.47it/s]" ] } ], @@ -6840,7 +4938,10 @@ " master_metadata_dictionary['nodes'][rna_id] = {\n", " 'Label': ''.join(lab),\n", " 'Description': ''.join(desc),\n", - " 'Synonym': '|'.join(syn)}" + " 'Synonym': '|'.join(syn)}\n", + "\n", + "# delete unneeded data\n", + "del rna_gene_data" ] }, { @@ -6854,7 +4955,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -6886,17 +4987,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 598286/598286 [03:42<00:00, 2691.06it/s]\n" - ] - } - ], + "outputs": [], "source": [ "# create metadata\n", "for idx, row in tqdm(var_metadata.iterrows(), total=var_metadata.shape[0]):\n", @@ -6919,7 +5012,10 @@ " 'Label': ''.join(lab),\n", " 'Description': ''.join(desc),\n", " 'Synonym': '|'.join(syn),\n", - " 'Dbxref': dbxref}" + " 'Dbxref': dbxref}\n", + "\n", + "# delete unneeded data\n", + "del var_metadata" ] }, { @@ -6933,7 +5029,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -6966,17 +5062,9 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 711/711 [03:48<00:00, 3.11it/s]\n" - ] - } - ], + "outputs": [], "source": [ "# get metadata\n", "nodes = list(set(reactome_pathways[0]) | set(reactome_pathways2[5]) | set(reactome_pathways3[1]))\n", @@ -6987,7 +5075,10 @@ "pathway_metadata_final.set_index('ID', inplace=True)\n", "\n", "# add entries to existing dictionary\n", - "master_metadata_dictionary['nodes'].update(pathway_metadata_final.to_dict('index'))" + "master_metadata_dictionary['nodes'].update(pathway_metadata_final.to_dict('index'))\n", + "\n", + "# delete unneeded data\n", + "del reactome_pathways, reactome_pathways2, reactome_pathways3" ] }, { @@ -7001,17 +5092,9 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "There are 8089 edges in the ontology (date:01/22/2022)\n" - ] - } - ], + "outputs": [], "source": [ "# download ontology\n", "if not os.path.exists(unprocessed_data_location + 'ro_with_imports.owl'):\n", @@ -7025,17 +5108,9 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 610/610 [00:00<00:00, 5187.92it/s]\n" - ] - } - ], + "outputs": [], "source": [ "# get metadata\n", "relation_metadata_dict, obo = {}, Namespace('http://purl.obolibrary.org/obo/')\n", @@ -7061,7 +5136,10 @@ " }\n", "\n", "# add entries to existing dictionary\n", - "master_metadata_dictionary['relations'].update(relation_metadata_dict)" + "master_metadata_dictionary['relations'].update(relation_metadata_dict)\n", + "\n", + "# delete unneeded data\n", + "del ro_graph" ] }, { @@ -7096,7 +5174,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -7152,24 +5230,52 @@ "source": [ "\n", "\n", - "##### Genomic Entity Metadata\n", - "\n", - "Process the dictionary created in the prior steps in order to assist with creating a master metadata file for all nodes that are a genomic entity (i.e., genes, transcripts, or proteins)." + "##### Genomic Entity Metadata<a class=\"anchor\" id=\"genomicinfo\"></a> \n", + "\n", + "Process the dictionary created in the prior steps in order to assist with creating a master metadata file for all nodes that are a genomic entity (i.e., genes, transcripts, or proteins). Some example output is shown below:\n", + "\n", + "``` python\n", + "{'NCBIGene_51471': {\n", + " 'Synonyms': ['acetyltransferase 1',\n", + " 'Hcml2',\n", + " 'probable N-acetyltransferase 8B',\n", + " 'CML2',\n", + " 'putative N-acetyltransferase 8B',\n", + " 'ATase1',\n", + " 'N-acetyltransferase 8B (putative, gene/pseudogene)',\n", + " 'N-acetyltransferase Camello 2',\n", + " 'NAT8BP',\n", + " 'camello-like protein 2',\n", + " 'N-acetyltransferase 8B (GCN5-related, putative, gene/pseudogene)',\n", + " 'putative N-acetyltransferase 8B',\n", + " 'ATase1',\n", + " 'N-acetyltransferase 8B (GCN5-related, putative, gene/pseudogene)',\n", + " 'N-acetyltransferase Camello 2',\n", + " 'acetyltransferase 1',\n", + " 'camello-like protein 2',\n", + " 'probable N-acetyltransferase 8B'],\n", + " 'PR': ['PR_Q9UHF3'],\n", + " 'GeneSymbol': ['GeneSymbol_NAT8BP',\n", + " 'GeneSymbol_Hcml2',\n", + " 'GeneSymbol_CML2',\n", + " 'GeneSymbol_NAT8B'],\n", + " 'ensembl gene': ['ensembl_ENSG00000204872'],\n", + " 'ensembl protein': ['ensembl_ENSP00000485054'],\n", + " 'map_location': ['2p13.1'],\n", + " 'Label': ['N-acetyltransferase 8B (putative, gene/pseudogene)'],\n", + " 'ensembl transcript': ['ensembl_ENST00000377712'],\n", + " 'transcript_name': ['NAT8B-201'],\n", + " 'HGNC_ID': ['HGNC_ID_30235'],\n", + " 'chromosome': ['2'],\n", + " 'uniprot': ['uniprot_Q9UHF3']}}\n", + "```" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 876333/876333 [00:30<00:00, 28512.58it/s] \n" - ] - } - ], + "outputs": [], "source": [ "# clean up data for use with master metadata\n", "genomic_metadata = dict()\n", @@ -7201,7 +5307,10 @@ " if isinstance(j , list): master_metadata_dict[updated_key][new_i] += j\n", " else: master_metadata_dict[updated_key][new_i] += [j]\n", " else: master_metadata_dict[updated_key][new_i] = [j]\n", - " genomic_metadata[updated_key] = master_metadata_dict" + " genomic_metadata[updated_key] = master_metadata_dict\n", + " \n", + "# delete unneeded data\n", + "del reformatted_mapped_identifiers" ] }, { @@ -7223,10 +5332,16 @@ "- RNA: [ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt)\n", "\n", "This chunk process the [`CTD_chem_gene_ixns.tsv`](http://ctdbase.org/reports/CTD_chem_gene_ixns.tsv.gz) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + "_chemical_ \n", " - `ChemicalID`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, MeSH Identifier. Variable is provided as a string without a prefix. \n", " - `CasRN`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, a string containing a CAS Registry Number, if available. \n", - " - `ChemicalName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. If not, a string containing the name of the chemical. \n", + " - `ChemicalName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. If not, a string containing the name of the chemical. \n", + " \n", + " _Gene, RNA, and Protein_ \n", + " - `GenomicInformation`: A dictionary of gene, RNA, and protein identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", + "\n", + "\n", "- **Edges:** \n", " - `Interaction`: A string describing a chemical-gene/protein/rna interaction. \n", " - `InteractionActions`: A \"|\"-delimited list of the actions that underlie an interaction. \n", @@ -7235,7 +5350,7 @@ }, { "cell_type": "code", - "execution_count": 438, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -7266,175 +5381,9 @@ }, { "cell_type": "code", - "execution_count": 439, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th># ChemicalName</th>\n", - " <th>ChemicalID</th>\n", - " <th>CasRN</th>\n", - " <th>GeneSymbol</th>\n", - " <th>GeneID</th>\n", - " <th>GeneForms</th>\n", - " <th>Organism</th>\n", - " <th>OrganismID</th>\n", - " <th>Interaction</th>\n", - " <th>InteractionActions</th>\n", - " <th>...</th>\n", - " <th>Ensembl_Transcript_IDs</th>\n", - " <th>Entrez_Gene_Type_x</th>\n", - " <th>Ensembl_Transcript_Type</th>\n", - " <th>Master_Gene_Type</th>\n", - " <th>Master_Transcript_Type</th>\n", - " <th>Entrez_Gene_prefix</th>\n", - " <th>Gene_IDs</th>\n", - " <th>Protein_Ontology_IDs</th>\n", - " <th>Entrez_Gene_Type_y</th>\n", - " <th>Entrez_Gene_Prefix</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", - " <td>MESH:C004822</td>\n", - " <td>35079-97-1</td>\n", - " <td>EPHX1</td>\n", - " <td>2052</td>\n", - " <td>gene</td>\n", - " <td>Homo sapiens</td>\n", - " <td>9606</td>\n", - " <td>[EPHX1 gene SNP affects the metabolism of carb...</td>\n", - " <td>affects^chemical synthesis|affects^metabolic p...</td>\n", - " <td>...</td>\n", - " <td>ensembl_ENST00000467015</td>\n", - " <td>protein-coding</td>\n", - " <td>processed_transcript</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " <td>NCBIGene_2052</td>\n", - " <td>2052</td>\n", - " <td>PR_P07099</td>\n", - " <td>protein-coding</td>\n", - " <td>NCBIGene_2052</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", - " <td>MESH:C004822</td>\n", - " <td>35079-97-1</td>\n", - " <td>EPHX1</td>\n", - " <td>2052</td>\n", - " <td>gene</td>\n", - " <td>Homo sapiens</td>\n", - " <td>9606</td>\n", - " <td>[EPHX1 gene SNP affects the metabolism of carb...</td>\n", - " <td>affects^chemical synthesis|affects^metabolic p...</td>\n", - " <td>...</td>\n", - " <td>ensembl_ENST00000366837</td>\n", - " <td>protein-coding</td>\n", - " <td>protein_coding</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " <td>NCBIGene_2052</td>\n", - " <td>2052</td>\n", - " <td>PR_P07099</td>\n", - " <td>protein-coding</td>\n", - " <td>NCBIGene_2052</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", - " <td>MESH:C004822</td>\n", - " <td>35079-97-1</td>\n", - " <td>EPHX1</td>\n", - " <td>2052</td>\n", - " <td>gene</td>\n", - " <td>Homo sapiens</td>\n", - " <td>9606</td>\n", - " <td>[EPHX1 gene SNP affects the metabolism of carb...</td>\n", - " <td>affects^chemical synthesis|affects^metabolic p...</td>\n", - " <td>...</td>\n", - " <td>ensembl_ENST00000272167</td>\n", - " <td>protein-coding</td>\n", - " <td>protein_coding</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " <td>NCBIGene_2052</td>\n", - " <td>2052</td>\n", - " <td>PR_P07099</td>\n", - " <td>protein-coding</td>\n", - " <td>NCBIGene_2052</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "<p>3 rows × 24 columns</p>\n", - "</div>" - ], - "text/plain": [ - " # ChemicalName ChemicalID \\\n", - "0 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", - "1 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", - "2 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", - "\n", - " CasRN GeneSymbol GeneID GeneForms Organism OrganismID \\\n", - "0 35079-97-1 EPHX1 2052 gene Homo sapiens 9606 \n", - "1 35079-97-1 EPHX1 2052 gene Homo sapiens 9606 \n", - "2 35079-97-1 EPHX1 2052 gene Homo sapiens 9606 \n", - "\n", - " Interaction \\\n", - "0 [EPHX1 gene SNP affects the metabolism of carb... \n", - "1 [EPHX1 gene SNP affects the metabolism of carb... \n", - "2 [EPHX1 gene SNP affects the metabolism of carb... \n", - "\n", - " InteractionActions ... \\\n", - "0 affects^chemical synthesis|affects^metabolic p... ... \n", - "1 affects^chemical synthesis|affects^metabolic p... ... \n", - "2 affects^chemical synthesis|affects^metabolic p... ... \n", - "\n", - " Ensembl_Transcript_IDs Entrez_Gene_Type_x Ensembl_Transcript_Type \\\n", - "0 ensembl_ENST00000467015 protein-coding processed_transcript \n", - "1 ensembl_ENST00000366837 protein-coding protein_coding \n", - "2 ensembl_ENST00000272167 protein-coding protein_coding \n", - "\n", - " Master_Gene_Type Master_Transcript_Type Entrez_Gene_prefix Gene_IDs \\\n", - "0 protein-coding protein-coding NCBIGene_2052 2052 \n", - "1 protein-coding protein-coding NCBIGene_2052 2052 \n", - "2 protein-coding protein-coding NCBIGene_2052 2052 \n", - "\n", - " Protein_Ontology_IDs Entrez_Gene_Type_y Entrez_Gene_Prefix \n", - "0 PR_P07099 protein-coding NCBIGene_2052 \n", - "1 PR_P07099 protein-coding NCBIGene_2052 \n", - "2 PR_P07099 protein-coding NCBIGene_2052 \n", - "\n", - "[3 rows x 24 columns]" - ] - }, - "execution_count": 439, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# merge identifier maps\n", "ctd_gene_inx = ctd_gene_inx.merge(mesh_chebi_map, left_on='ChemicalID', right_on='MESH_ID')\n", @@ -7454,17 +5403,9 @@ }, { "cell_type": "code", - "execution_count": 440, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 8165729/8165729 [1:39:33<00:00, 1366.94it/s] \n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'] = {'chemical-gene': {}, 'chemical-rna': {}, 'chemical-protein': {}}\n", "\n", @@ -7547,12 +5488,17 @@ "- Chemicals: [MESH_CHEBI_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/MESH_CHEBI_MAP.txt) \n", "\n", "This chunk process the [`CTD_chem_go_enriched.tsv`](http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + " _Chemical_ \n", " - `ChemicalID`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, MeSH Identifier. Variable is provided as a string without a prefix. \n", " - `CasRN`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, a string containing a CAS Registry Number, if available. \n", - " - `ChemicalName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. If not, a string containing the name of the chemical. \n", + " - `ChemicalName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. If not, a string containing the name of the chemical. \n", + " \n", + " _GO Biological Process, Cellular Component, Molecular Function_ \n", " - `GOTermName`: A string containing the concept's synonym. \n", " - `Ontology`: A string naming the GO Ontology subset. \n", + "\n", + "\n", "- **Edges:** \n", " - `HighestGOLevel`: The highest level to which the GO term is assigned within the GO hierarchical ontology. Many GO terms are located at multiple levels within the ontology; only the highest level is displayed. Level 1 constitutes “children” of the most general Biological Process, Cellular Component, and Molecular Function terms. Source: http://ctdbase.org/help/chemGODetailHelp.jsp. \n", " - `Pvalue`: Raw P-value. Source: http://ctdbase.org/help/chemGODetailHelp.jsp. \n", @@ -7565,7 +5511,7 @@ }, { "cell_type": "code", - "execution_count": 441, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -7592,133 +5538,9 @@ }, { "cell_type": "code", - "execution_count": 442, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th># ChemicalName</th>\n", - " <th>ChemicalID</th>\n", - " <th>CasRN</th>\n", - " <th>Ontology</th>\n", - " <th>GOTermName</th>\n", - " <th>GOTermID</th>\n", - " <th>HighestGOLevel</th>\n", - " <th>PValue</th>\n", - " <th>CorrectedPValue</th>\n", - " <th>TargetMatchQty</th>\n", - " <th>TargetTotalQty</th>\n", - " <th>BackgroundMatchQty</th>\n", - " <th>BackgroundTotalQty</th>\n", - " <th>MESH_ID</th>\n", - " <th>CHEBI_ID</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>1,10-phenanthroline</td>\n", - " <td>MESH:C025205</td>\n", - " <td>66-71-7</td>\n", - " <td>Biological Process</td>\n", - " <td>ADP metabolic process</td>\n", - " <td>GO_0046031</td>\n", - " <td>7.0</td>\n", - " <td>1.720000e-21</td>\n", - " <td>7.570000e-18</td>\n", - " <td>13.0</td>\n", - " <td>81.0</td>\n", - " <td>92.0</td>\n", - " <td>44536.0</td>\n", - " <td>MESH:C025205</td>\n", - " <td>CHEBI_44975</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>1,10-phenanthroline</td>\n", - " <td>MESH:C025205</td>\n", - " <td>66-71-7</td>\n", - " <td>Biological Process</td>\n", - " <td>aging</td>\n", - " <td>GO_0007568</td>\n", - " <td>2.0</td>\n", - " <td>5.600000e-16</td>\n", - " <td>2.460000e-12</td>\n", - " <td>14.0</td>\n", - " <td>81.0</td>\n", - " <td>310.0</td>\n", - " <td>44536.0</td>\n", - " <td>MESH:C025205</td>\n", - " <td>CHEBI_44975</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>1,10-phenanthroline</td>\n", - " <td>MESH:C025205</td>\n", - " <td>66-71-7</td>\n", - " <td>Biological Process</td>\n", - " <td>alcohol metabolic process</td>\n", - " <td>GO_0006066</td>\n", - " <td>3.0</td>\n", - " <td>1.000000e-12</td>\n", - " <td>4.410000e-09</td>\n", - " <td>12.0</td>\n", - " <td>81.0</td>\n", - " <td>330.0</td>\n", - " <td>44536.0</td>\n", - " <td>MESH:C025205</td>\n", - " <td>CHEBI_44975</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " # ChemicalName ChemicalID CasRN Ontology \\\n", - "0 1,10-phenanthroline MESH:C025205 66-71-7 Biological Process \n", - "1 1,10-phenanthroline MESH:C025205 66-71-7 Biological Process \n", - "2 1,10-phenanthroline MESH:C025205 66-71-7 Biological Process \n", - "\n", - " GOTermName GOTermID HighestGOLevel PValue \\\n", - "0 ADP metabolic process GO_0046031 7.0 1.720000e-21 \n", - "1 aging GO_0007568 2.0 5.600000e-16 \n", - "2 alcohol metabolic process GO_0006066 3.0 1.000000e-12 \n", - "\n", - " CorrectedPValue TargetMatchQty TargetTotalQty BackgroundMatchQty \\\n", - "0 7.570000e-18 13.0 81.0 92.0 \n", - "1 2.460000e-12 14.0 81.0 310.0 \n", - "2 4.410000e-09 12.0 81.0 330.0 \n", - "\n", - " BackgroundTotalQty MESH_ID CHEBI_ID \n", - "0 44536.0 MESH:C025205 CHEBI_44975 \n", - "1 44536.0 MESH:C025205 CHEBI_44975 \n", - "2 44536.0 MESH:C025205 CHEBI_44975 " - ] - }, - "execution_count": 442, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# merge identifier maps\n", "ctd_chem_go = ctd_chem_go.merge(mesh_chebi_map, left_on='ChemicalID', right_on='MESH_ID')\n", @@ -7736,17 +5558,9 @@ }, { "cell_type": "code", - "execution_count": 443, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 3910803/3910803 [29:32<00:00, 2205.92it/s] \n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'chemical-gobp': {}, 'chemical-gocc': {}, 'chemical-gomf': {}})\n", "\n", @@ -7827,13 +5641,18 @@ "- Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) \n", "\n", "This chunk process the [`CTD_chemicals_diseases.tsv`](http://ctdbase.org/reports/CTD_chemicals_diseases.tsv.gz) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + " _Chemical_ \n", " - `ChemicalID`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, MeSH Identifier. Variable is provided as a string without a prefix. \n", " - `CasRN`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. If not, a string containing a CAS Registry Number, if available. \n", " - `ChemicalName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. If not, a string containing the name of the chemical. \n", + " \n", + " _Disease, Phenotype_ \n", " - `DiseaseName`: A string containing the concept's synonym. \n", " - `DiseaseID`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", " - `OmimIDs`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", + "\n", + "\n", "- **Edges:** \n", " - `DirectEvidence`: '|'-delimited list of strings that include keywords. \n", " - `InferenceScore`: The inference score (float) reflects the degree of similarity between CTD chemical–gene–disease networks and a similar scale-free random network. The higher the score, the more likely the inference network has atypical connectivity. \n", @@ -7843,7 +5662,7 @@ }, { "cell_type": "code", - "execution_count": 444, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -7870,137 +5689,9 @@ }, { "cell_type": "code", - "execution_count": 445, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th># ChemicalName</th>\n", - " <th>ChemicalID</th>\n", - " <th>CasRN</th>\n", - " <th>DiseaseName</th>\n", - " <th>DiseaseID</th>\n", - " <th>DirectEvidence</th>\n", - " <th>InferenceGeneSymbol</th>\n", - " <th>InferenceScore</th>\n", - " <th>OmimIDs</th>\n", - " <th>PubMedIDs</th>\n", - " <th>MESH_ID</th>\n", - " <th>CHEBI_ID</th>\n", - " <th>Disease_IDs_x</th>\n", - " <th>MONDO_IDs</th>\n", - " <th>Disease_IDs_y</th>\n", - " <th>HP_IDs</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", - " <td>MESH:C004822</td>\n", - " <td>35079-97-1</td>\n", - " <td>Carcinoma</td>\n", - " <td>MESH:D002277</td>\n", - " <td>NaN</td>\n", - " <td>EPHX1</td>\n", - " <td>5.06</td>\n", - " <td>NaN</td>\n", - " <td>12376462</td>\n", - " <td>MESH:C004822</td>\n", - " <td>CHEBI_4592</td>\n", - " <td>MESH:D002277</td>\n", - " <td>MONDO_0006406</td>\n", - " <td>MESH:D002277</td>\n", - " <td>HP_0030731</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine...</td>\n", - " <td>MESH:C004822</td>\n", - " <td>35079-97-1</td>\n", - " <td>Carcinoma</td>\n", - " <td>MESH:D002277</td>\n", - " <td>NaN</td>\n", - " <td>EPHX1</td>\n", - " <td>5.06</td>\n", - " <td>NaN</td>\n", - " <td>12376462</td>\n", - " <td>MESH:C004822</td>\n", - " <td>CHEBI_4592</td>\n", - " <td>MESH:D002277</td>\n", - " <td>MONDO_0004993</td>\n", - " <td>MESH:D002277</td>\n", - " <td>HP_0030731</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>10,11-dihydro-10-hydroxycarbamazepine</td>\n", - " <td>MESH:C039775</td>\n", - " <td>NaN</td>\n", - " <td>Carcinoma</td>\n", - " <td>MESH:D002277</td>\n", - " <td>NaN</td>\n", - " <td>ABCB1</td>\n", - " <td>4.24</td>\n", - " <td>NaN</td>\n", - " <td>21332314</td>\n", - " <td>MESH:C039775</td>\n", - " <td>CHEBI_701</td>\n", - " <td>MESH:D002277</td>\n", - " <td>MONDO_0006406</td>\n", - " <td>MESH:D002277</td>\n", - " <td>HP_0030731</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " # ChemicalName ChemicalID \\\n", - "0 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", - "1 10,11-dihydro-10,11-dihydroxy-5H-dibenzazepine... MESH:C004822 \n", - "2 10,11-dihydro-10-hydroxycarbamazepine MESH:C039775 \n", - "\n", - " CasRN DiseaseName DiseaseID DirectEvidence InferenceGeneSymbol \\\n", - "0 35079-97-1 Carcinoma MESH:D002277 NaN EPHX1 \n", - "1 35079-97-1 Carcinoma MESH:D002277 NaN EPHX1 \n", - "2 NaN Carcinoma MESH:D002277 NaN ABCB1 \n", - "\n", - " InferenceScore OmimIDs PubMedIDs MESH_ID CHEBI_ID Disease_IDs_x \\\n", - "0 5.06 NaN 12376462 MESH:C004822 CHEBI_4592 MESH:D002277 \n", - "1 5.06 NaN 12376462 MESH:C004822 CHEBI_4592 MESH:D002277 \n", - "2 4.24 NaN 21332314 MESH:C039775 CHEBI_701 MESH:D002277 \n", - "\n", - " MONDO_IDs Disease_IDs_y HP_IDs \n", - "0 MONDO_0006406 MESH:D002277 HP_0030731 \n", - "1 MONDO_0004993 MESH:D002277 HP_0030731 \n", - "2 MONDO_0006406 MESH:D002277 HP_0030731 " - ] - }, - "execution_count": 445, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ctd_chem_dis = ctd_chem_dis.merge(mesh_chebi_map, left_on='ChemicalID', right_on='MESH_ID')\n", "ctd_chem_dis = ctd_chem_dis.merge(disease_maps, left_on='DiseaseID', right_on='Disease_IDs')\n", @@ -8019,17 +5710,9 @@ }, { "cell_type": "code", - "execution_count": 446, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 5127731/5127731 [1:19:02<00:00, 1081.22it/s]\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'chemical-disease': {}, 'chemical-phenotype': {}})\n", "\n", @@ -8108,100 +5791,20 @@ "- `chemical-pathway` \n", "\n", "This chunk process the [`ChEBI2Reactome_All_Levels.txt`](https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt) file and obtains the following node metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + " _Pathway_ \n", " - `DBReference`: A string containing the concept's database cross-reference, which is formatted as prefix:ID. \n", + "\n", + "\n", "- **Edges:** \n", " - `EvidenceID`: A string containing an evidence code." ] }, { "cell_type": "code", - "execution_count": 447, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>0</th>\n", - " <th>1</th>\n", - " <th>2</th>\n", - " <th>3</th>\n", - " <th>4</th>\n", - " <th>5</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>16</th>\n", - " <td>CHEBI_10033</td>\n", - " <td>reactome_R-HSA-1430728</td>\n", - " <td>https://reactome.org/PathwayBrowser/#/R-HSA-14...</td>\n", - " <td>Metabolism</td>\n", - " <td>TAS</td>\n", - " <td>Homo sapiens</td>\n", - " </tr>\n", - " <tr>\n", - " <th>17</th>\n", - " <td>CHEBI_10033</td>\n", - " <td>reactome_R-HSA-196854</td>\n", - " <td>https://reactome.org/PathwayBrowser/#/R-HSA-19...</td>\n", - " <td>Metabolism of vitamins and cofactors</td>\n", - " <td>TAS</td>\n", - " <td>Homo sapiens</td>\n", - " </tr>\n", - " <tr>\n", - " <th>18</th>\n", - " <td>CHEBI_10033</td>\n", - " <td>reactome_R-HSA-6806664</td>\n", - " <td>https://reactome.org/PathwayBrowser/#/R-HSA-68...</td>\n", - " <td>Metabolism of vitamin K</td>\n", - " <td>TAS</td>\n", - " <td>Homo sapiens</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " 0 1 \\\n", - "16 CHEBI_10033 reactome_R-HSA-1430728 \n", - "17 CHEBI_10033 reactome_R-HSA-196854 \n", - "18 CHEBI_10033 reactome_R-HSA-6806664 \n", - "\n", - " 2 \\\n", - "16 https://reactome.org/PathwayBrowser/#/R-HSA-14... \n", - "17 https://reactome.org/PathwayBrowser/#/R-HSA-19... \n", - "18 https://reactome.org/PathwayBrowser/#/R-HSA-68... \n", - "\n", - " 3 4 5 \n", - "16 Metabolism TAS Homo sapiens \n", - "17 Metabolism of vitamins and cofactors TAS Homo sapiens \n", - "18 Metabolism of vitamin K TAS Homo sapiens " - ] - }, - "execution_count": 447, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# download data\n", "url = 'https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt'\n", @@ -8229,17 +5832,9 @@ }, { "cell_type": "code", - "execution_count": 448, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 33628/33628 [00:10<00:00, 3269.54it/s]\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'chemical-pathway': {}})\n", "\n", @@ -8287,13 +5882,18 @@ "**Identifier Maps:** \n", "- Proteins: [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", "This chunk process the [`goa_human.gaf`](http://current.geneontology.org/annotations/goa_human.gaf.gz) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", - " - `Aspect`: A variable that indicates what species the annotation applies to. \n", + "- **Nodes:** \n", + "_GO Biological Process, Cellular Component, and Molecular Function_ \n", " - `DB_Object_Name`: A string containing the concept's synonym. \n", " - `DB_Object_Synonym`: A string containing the concept's synonym. \n", " - `DB_Object_Symbol`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", " - `With_Or_From`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", " - `DB_Object_Type`: A string indicating the type of object that has been annotated. \n", + " \n", + " _Protein_ \n", + " - `GenomicInformation`: A dictionary of protein identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", + "\n", + "\n", "- **Edges:** \n", " - `Qualifier`: Some annotations are modified by qualifiers, which have specific usage rules and meanings within GO. \n", " - `DB_Reference`: One or more unique identifiers for a single source cited as an authority for the attribution of the GO ID to the DB Object ID. This may be a literature reference or a database record. The syntax is DB:accession_number. \n", @@ -8329,7 +5929,7 @@ }, { "cell_type": "code", - "execution_count": 449, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -8357,149 +5957,9 @@ }, { "cell_type": "code", - "execution_count": 450, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>0</th>\n", - " <th>1</th>\n", - " <th>2</th>\n", - " <th>3</th>\n", - " <th>4</th>\n", - " <th>5</th>\n", - " <th>6</th>\n", - " <th>7</th>\n", - " <th>8</th>\n", - " <th>9</th>\n", - " <th>10</th>\n", - " <th>11</th>\n", - " <th>12</th>\n", - " <th>13</th>\n", - " <th>14</th>\n", - " <th>15</th>\n", - " <th>16</th>\n", - " <th>Uniprot_Accession_IDs</th>\n", - " <th>Protein_Ontology_IDs</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>UniProtKB</td>\n", - " <td>A0A024RBG1</td>\n", - " <td>NUDT4B</td>\n", - " <td>enables</td>\n", - " <td>GO_0003723</td>\n", - " <td>GO_REF:0000043</td>\n", - " <td>IEA</td>\n", - " <td>UniProtKB-KW:KW-0694</td>\n", - " <td>F</td>\n", - " <td>Diphosphoinositol polyphosphate phosphohydrola...</td>\n", - " <td>NUDT4B</td>\n", - " <td>protein</td>\n", - " <td>taxon:9606</td>\n", - " <td>20211010</td>\n", - " <td>UniProt</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>A0A024RBG1</td>\n", - " <td>PR_A0A024RBG1</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>UniProtKB</td>\n", - " <td>A0A024RBG1</td>\n", - " <td>NUDT4B</td>\n", - " <td>enables</td>\n", - " <td>GO_0046872</td>\n", - " <td>GO_REF:0000043</td>\n", - " <td>IEA</td>\n", - " <td>UniProtKB-KW:KW-0479</td>\n", - " <td>F</td>\n", - " <td>Diphosphoinositol polyphosphate phosphohydrola...</td>\n", - " <td>NUDT4B</td>\n", - " <td>protein</td>\n", - " <td>taxon:9606</td>\n", - " <td>20211010</td>\n", - " <td>UniProt</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>A0A024RBG1</td>\n", - " <td>PR_A0A024RBG1</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>UniProtKB</td>\n", - " <td>A0A024RBG1</td>\n", - " <td>NUDT4B</td>\n", - " <td>enables</td>\n", - " <td>GO_0052840</td>\n", - " <td>GO_REF:0000003</td>\n", - " <td>IEA</td>\n", - " <td>EC:3.6.1.52</td>\n", - " <td>F</td>\n", - " <td>Diphosphoinositol polyphosphate phosphohydrola...</td>\n", - " <td>NUDT4B</td>\n", - " <td>protein</td>\n", - " <td>taxon:9606</td>\n", - " <td>20211009</td>\n", - " <td>UniProt</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>A0A024RBG1</td>\n", - " <td>PR_A0A024RBG1</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " 0 1 2 3 4 5 6 \\\n", - "0 UniProtKB A0A024RBG1 NUDT4B enables GO_0003723 GO_REF:0000043 IEA \n", - "1 UniProtKB A0A024RBG1 NUDT4B enables GO_0046872 GO_REF:0000043 IEA \n", - "2 UniProtKB A0A024RBG1 NUDT4B enables GO_0052840 GO_REF:0000003 IEA \n", - "\n", - " 7 8 9 \\\n", - "0 UniProtKB-KW:KW-0694 F Diphosphoinositol polyphosphate phosphohydrola... \n", - "1 UniProtKB-KW:KW-0479 F Diphosphoinositol polyphosphate phosphohydrola... \n", - "2 EC:3.6.1.52 F Diphosphoinositol polyphosphate phosphohydrola... \n", - "\n", - " 10 11 12 13 14 15 16 \\\n", - "0 NUDT4B protein taxon:9606 20211010 UniProt None None \n", - "1 NUDT4B protein taxon:9606 20211010 UniProt None None \n", - "2 NUDT4B protein taxon:9606 20211009 UniProt None None \n", - "\n", - " Uniprot_Accession_IDs Protein_Ontology_IDs \n", - "0 A0A024RBG1 PR_A0A024RBG1 \n", - "1 A0A024RBG1 PR_A0A024RBG1 \n", - "2 A0A024RBG1 PR_A0A024RBG1 " - ] - }, - "execution_count": 450, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "goa_gene = goa_gene.merge(uniprot_pro_map, left_on=1, right_on='Uniprot_Accession_IDs')\n", "\n", @@ -8516,17 +5976,9 @@ }, { "cell_type": "code", - "execution_count": 451, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 613887/613887 [04:24<00:00, 2320.65it/s]\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'protein-gobp': {}, 'protein-gocc': {}, 'protein-gomf': {}})\n", "\n", @@ -8545,22 +5997,19 @@ " # add pr information\n", " if pr in master_metadata_dictionary['nodes'].keys(): \n", " if url in master_metadata_dictionary['nodes'][pr].keys():\n", - " master_metadata_dictionary['nodes'][pr][url]['GOA_Aspect'] |= {aspect}\n", " master_metadata_dictionary['nodes'][pr][url]['GOA_DB_Object_Name'] |= {pr_db}\n", " master_metadata_dictionary['nodes'][pr][url]['GOA_DB_Object_Synonym'] |= {pr_syn}\n", " master_metadata_dictionary['nodes'][pr][url]['GOA_DB_Object_Symbol'] |= {pr_symb}\n", " master_metadata_dictionary['nodes'][pr][url]['GOA_With_Or_From'] |= {db_with}\n", " else:\n", " master_metadata_dictionary['nodes'][pr].update({\n", - " url: {'GOA_Aspect': {aspect},\n", - " 'GOA_DB_Object_Name': {pr_db},\n", + " url: {'GOA_DB_Object_Name': {pr_db},\n", " 'GOA_DB_Object_Synonym': {pr_syn},\n", " 'GOA_DB_Object_Symbol': {pr_symb},\n", " 'GOA_With_Or_From': {db_with}}})\n", " else:\n", " master_metadata_dictionary['nodes'].update({pr: {\n", - " url: {'GOA_Aspect': {aspect},\n", - " 'GOA_DB_Object_Name': {pr_db},\n", + " url: {'GOA_DB_Object_Name': {pr_db},\n", " 'GOA_DB_Object_Synonym': {pr_syn},\n", " 'GOA_DB_Object_Symbol': {pr_symb},\n", " 'GOA_With_Or_From': {db_with}}}})\n", @@ -8602,13 +6051,16 @@ "- Genes: [UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt) \n", "\n", "This chunk process the [`COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt`](http://genemania.org/data/current/Homo_sapiens.COMBINED/COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt) file and obtains the following edge metadata: \n", + "- **Nodes:** \n", + " _Genes_ \n", + " - `GenomicInformation`: A dictionary of gene identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", "- **Edges:** \n", - " - `Weight`: Assumes the input gene list is related through GO biological processes. The score will vary depending on the type of network, but in general is a number ranging from zero (no interaction) to 1 (strong interaction). See PMID: 25254104 for more detail. " + " - `Weight`: Assumes the input gene list is related through GO biological processes. The score will vary depending on the type of network, but in general is a number ranging from zero (no interaction) to 1 (strong interaction). See `PMID:25254104` for more detail. " ] }, { "cell_type": "code", - "execution_count": 459, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -8630,112 +6082,9 @@ }, { "cell_type": "code", - "execution_count": 460, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>Gene_A</th>\n", - " <th>Gene_B</th>\n", - " <th>Weight</th>\n", - " <th>Uniprot_Accession_IDs_x</th>\n", - " <th>Entrez_Gene_A</th>\n", - " <th>master_gene_type_x</th>\n", - " <th>gene_type_update_x</th>\n", - " <th>Uniprot_Accession_IDs_y</th>\n", - " <th>Entrez_Gene_B</th>\n", - " <th>master_gene_type_y</th>\n", - " <th>gene_type_update_y</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>O60762</td>\n", - " <td>P48506</td>\n", - " <td>2.700000e-05</td>\n", - " <td>O60762</td>\n", - " <td>NCBIGene_8813</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " <td>P48506</td>\n", - " <td>NCBIGene_2729</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>Q8IZE3</td>\n", - " <td>P48506</td>\n", - " <td>6.800000e-08</td>\n", - " <td>Q8IZE3</td>\n", - " <td>NCBIGene_57147</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " <td>P48506</td>\n", - " <td>NCBIGene_2729</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>Q9NSG2</td>\n", - " <td>P48506</td>\n", - " <td>1.100000e-07</td>\n", - " <td>Q9NSG2</td>\n", - " <td>NCBIGene_55732</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " <td>P48506</td>\n", - " <td>NCBIGene_2729</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " Gene_A Gene_B Weight Uniprot_Accession_IDs_x Entrez_Gene_A \\\n", - "0 O60762 P48506 2.700000e-05 O60762 NCBIGene_8813 \n", - "1 Q8IZE3 P48506 6.800000e-08 Q8IZE3 NCBIGene_57147 \n", - "2 Q9NSG2 P48506 1.100000e-07 Q9NSG2 NCBIGene_55732 \n", - "\n", - " master_gene_type_x gene_type_update_x Uniprot_Accession_IDs_y \\\n", - "0 protein-coding protein-coding P48506 \n", - "1 protein-coding protein-coding P48506 \n", - "2 protein-coding protein-coding P48506 \n", - "\n", - " Entrez_Gene_B master_gene_type_y gene_type_update_y \n", - "0 NCBIGene_2729 protein-coding protein-coding \n", - "1 NCBIGene_2729 protein-coding protein-coding \n", - "2 NCBIGene_2729 protein-coding protein-coding " - ] - }, - "execution_count": 460, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "gm_gene_gene = gm_gene_gene.merge(uniprot_entrez_data, left_on='Gene_A', right_on='Uniprot_Accession_IDs')\n", "gm_gene_gene.rename(columns={'Entrez_Gene_IDs': 'Entrez_Gene_A'}, inplace=True)\n", @@ -8755,17 +6104,9 @@ }, { "cell_type": "code", - "execution_count": 461, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 11706791/11706791 [59:03<00:00, 3303.94it/s] \n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'gene-gene': {}})\n", "\n", @@ -8811,8 +6152,11 @@ "- Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt) \n", "\n", "This chunk process the [`phenotype.hpoa`](http://purl.obolibrary.org/obo/hp/hpoa/phenotype.hpoa) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + " _Disease, Phenotype_ \n", " - `DiseaseName`: A string containing the concept's synonym. \n", + "\n", + "\n", "- **Edges:** \n", " - `Reference`: This required field indicates the source of the information used for the annotation. This may be the clinical experience of the annotator or may be taken from an article as indicated by a PubMed id. Each collaborating center of the Human Phenotype Ontology consortium is assigned a HPO:Ref id. In addition, if appropriate, a PubMed id for an article describing the clinical abnormality may be used. \n", " - `Evidence`: This required field indicates the level of evidence supporting the annotation. Annotations that have been extracted by parsing the Clinical Features sections of the omim.txt file are assigned the evidence code IEA. Other codes include PCS for published clinical study. This should be used for information extracted from articles in the medical literature. ICE can be used for annotations based on individual clinical experience. This may be appropriate for disorders with a limited amount of published data. This must be accompanied by an entry in the DB:Reference field denoting the individual or center performing the annotation together with an identifier. For instance, GH:007 might be used to refer to the seventh such annotation made by a specialist from Gotham Hospital (assuming the prefix GH has been registered with the HPO). Finally we have TAS, which stands for “traceable author statement”, usually reviews or disease entries (e.g. OMIM) that only refers to the original publication.. \n", @@ -8829,7 +6173,7 @@ }, { "cell_type": "code", - "execution_count": 455, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -8856,124 +6200,9 @@ }, { "cell_type": "code", - "execution_count": 456, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>#DatabaseID</th>\n", - " <th>DiseaseName</th>\n", - " <th>Qualifier</th>\n", - " <th>HPO_ID</th>\n", - " <th>Reference</th>\n", - " <th>Evidence</th>\n", - " <th>Onset</th>\n", - " <th>Frequency</th>\n", - " <th>Sex</th>\n", - " <th>Modifier</th>\n", - " <th>Aspect</th>\n", - " <th>Biocuration</th>\n", - " <th>Disease_IDs</th>\n", - " <th>MONDO_IDs</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>OMIM:116860</td>\n", - " <td>Cerebral cavernous malformations 1</td>\n", - " <td>None</td>\n", - " <td>HP_0001250</td>\n", - " <td>OMIM:116860</td>\n", - " <td>IEA</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>P</td>\n", - " <td>HPO:iea[2009-02-17]</td>\n", - " <td>OMIM:116860</td>\n", - " <td>MONDO_0031037</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>OMIM:116860</td>\n", - " <td>Cerebral cavernous malformations 1</td>\n", - " <td>None</td>\n", - " <td>HP_0003011</td>\n", - " <td>OMIM:116860</td>\n", - " <td>IEA</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>P</td>\n", - " <td>HPO:iea[2009-02-17]</td>\n", - " <td>OMIM:116860</td>\n", - " <td>MONDO_0031037</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>OMIM:116860</td>\n", - " <td>Cerebral cavernous malformations 1</td>\n", - " <td>None</td>\n", - " <td>HP_0003829</td>\n", - " <td>OMIM:116860</td>\n", - " <td>IEA</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>M</td>\n", - " <td>HPO:iea[2009-02-17]</td>\n", - " <td>OMIM:116860</td>\n", - " <td>MONDO_0031037</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " #DatabaseID DiseaseName Qualifier HPO_ID \\\n", - "0 OMIM:116860 Cerebral cavernous malformations 1 None HP_0001250 \n", - "1 OMIM:116860 Cerebral cavernous malformations 1 None HP_0003011 \n", - "2 OMIM:116860 Cerebral cavernous malformations 1 None HP_0003829 \n", - "\n", - " Reference Evidence Onset Frequency Sex Modifier Aspect \\\n", - "0 OMIM:116860 IEA None None None None P \n", - "1 OMIM:116860 IEA None None None None P \n", - "2 OMIM:116860 IEA None None None None M \n", - "\n", - " Biocuration Disease_IDs MONDO_IDs \n", - "0 HPO:iea[2009-02-17] OMIM:116860 MONDO_0031037 \n", - "1 HPO:iea[2009-02-17] OMIM:116860 MONDO_0031037 \n", - "2 HPO:iea[2009-02-17] OMIM:116860 MONDO_0031037 " - ] - }, - "execution_count": 456, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "hpo_dis_phe = hpo_dis_phe.merge(disease_maps, left_on='#DatabaseID', right_on='Disease_IDs')\n", "\n", @@ -8990,17 +6219,9 @@ }, { "cell_type": "code", - "execution_count": 458, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 180689/180689 [01:13<00:00, 2450.57it/s]\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'disease-phenotype': {}})\n", "\n", @@ -9048,9 +6269,14 @@ "- `pathway-gomf` \n", "\n", "This chunk process the [`gene_association.reactome.tsv`](https://reactome.org/download/current/gene_association.reactome.gz) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + " _Pathway_ \n", " - `DBReference`: A string containing the concept's database cross-reference, which is formatted as Prefix:ID. \n", + " \n", + " _Gene Ontology Biological Proces, Cellular Component, Molecular Function_\n", " - `Aspect`: A variable indicating what GO subontology is being used. \n", + "\n", + "\n", "- **Edges:** \n", " - `EvidenceCode`: Each annotation includes an evidence code to indicate how the annotation to a particular term is supported\n", " - Inferred from Experiment (EXP)\n", @@ -9085,136 +6311,9 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>0</th>\n", - " <th>1</th>\n", - " <th>2</th>\n", - " <th>3</th>\n", - " <th>4</th>\n", - " <th>5</th>\n", - " <th>6</th>\n", - " <th>7</th>\n", - " <th>8</th>\n", - " <th>9</th>\n", - " <th>10</th>\n", - " <th>11</th>\n", - " <th>12</th>\n", - " <th>13</th>\n", - " <th>14</th>\n", - " <th>15</th>\n", - " <th>16</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>UniProtKB</td>\n", - " <td>A0A075B6P5</td>\n", - " <td>KV228_HUMAN</td>\n", - " <td>located_in</td>\n", - " <td>GO_0005576</td>\n", - " <td>reactome_R-HSA-166753</td>\n", - " <td>TAS</td>\n", - " <td>None</td>\n", - " <td>C</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>protein</td>\n", - " <td>taxon:9606</td>\n", - " <td>20161111</td>\n", - " <td>Reactome</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>UniProtKB</td>\n", - " <td>A0A075B6P5</td>\n", - " <td>KV228_HUMAN</td>\n", - " <td>located_in</td>\n", - " <td>GO_0005576</td>\n", - " <td>reactome_R-HSA-166792</td>\n", - " <td>TAS</td>\n", - " <td>None</td>\n", - " <td>C</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>protein</td>\n", - " <td>taxon:9606</td>\n", - " <td>20161111</td>\n", - " <td>Reactome</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>UniProtKB</td>\n", - " <td>A0A075B6P5</td>\n", - " <td>KV228_HUMAN</td>\n", - " <td>located_in</td>\n", - " <td>GO_0005576</td>\n", - " <td>reactome_R-HSA-173626</td>\n", - " <td>TAS</td>\n", - " <td>None</td>\n", - " <td>C</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " <td>protein</td>\n", - " <td>taxon:9606</td>\n", - " <td>20161111</td>\n", - " <td>Reactome</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " 0 1 2 3 4 \\\n", - "0 UniProtKB A0A075B6P5 KV228_HUMAN located_in GO_0005576 \n", - "1 UniProtKB A0A075B6P5 KV228_HUMAN located_in GO_0005576 \n", - "2 UniProtKB A0A075B6P5 KV228_HUMAN located_in GO_0005576 \n", - "\n", - " 5 6 7 8 9 10 11 12 \\\n", - "0 reactome_R-HSA-166753 TAS None C None None protein taxon:9606 \n", - "1 reactome_R-HSA-166792 TAS None C None None protein taxon:9606 \n", - "2 reactome_R-HSA-173626 TAS None C None None protein taxon:9606 \n", - "\n", - " 13 14 15 16 \n", - "0 20161111 Reactome None None \n", - "1 20161111 Reactome None None \n", - "2 20161111 Reactome None None " - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# download data\n", "url = 'https://reactome.org/download/current/gene_association.reactome.gz'\n", @@ -9244,31 +6343,9 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/86795 [00:00<?, ?it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "pathway-gocc reactome_R-HSA-166753-GO_0005576 GO_0005576\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'gobp-pathway': {}, 'pathway-gocc': {}, 'pathway-gomf': {}})\n", "\n", @@ -9322,8 +6399,14 @@ "**Identifier Maps:** \n", "- Proteins: [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", "This chunk process the [`UniProt2Reactome_All_Levels.txt`](https://reactome.org/download/current/UniProt2Reactome_All_Levels.txt) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", - " - `PathwayName`: A string containing the concept's label. \n", + "- **Nodes:** \n", + " _Pathway_ \n", + " - `PathwayName`: A string containing the concept's label. \n", + " \n", + " _Protein_ \n", + " - `GenomicInformation`: A dictionary of protein identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", + "\n", + "\n", "- **Edges:** \n", " - `EvidenceID`: Each annotation includes an evidence code to indicate how the annotation to a particular term is supported\n", " - Inferred from Experiment (EXP)\n", @@ -9356,92 +6439,9 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>0</th>\n", - " <th>1</th>\n", - " <th>2</th>\n", - " <th>3</th>\n", - " <th>4</th>\n", - " <th>5</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>2095</th>\n", - " <td>A0A075B6P5</td>\n", - " <td>reactome_R-HSA-109582</td>\n", - " <td>https://reactome.org/PathwayBrowser/#/R-HSA-10...</td>\n", - " <td>Hemostasis</td>\n", - " <td>TAS</td>\n", - " <td>Homo sapiens</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2096</th>\n", - " <td>A0A075B6P5</td>\n", - " <td>reactome_R-HSA-1280218</td>\n", - " <td>https://reactome.org/PathwayBrowser/#/R-HSA-12...</td>\n", - " <td>Adaptive Immune System</td>\n", - " <td>TAS</td>\n", - " <td>Homo sapiens</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2097</th>\n", - " <td>A0A075B6P5</td>\n", - " <td>reactome_R-HSA-1280218</td>\n", - " <td>https://reactome.org/PathwayBrowser/#/R-HSA-12...</td>\n", - " <td>Adaptive Immune System</td>\n", - " <td>IEA</td>\n", - " <td>Homo sapiens</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " 0 1 \\\n", - "2095 A0A075B6P5 reactome_R-HSA-109582 \n", - "2096 A0A075B6P5 reactome_R-HSA-1280218 \n", - "2097 A0A075B6P5 reactome_R-HSA-1280218 \n", - "\n", - " 2 \\\n", - "2095 https://reactome.org/PathwayBrowser/#/R-HSA-10... \n", - "2096 https://reactome.org/PathwayBrowser/#/R-HSA-12... \n", - "2097 https://reactome.org/PathwayBrowser/#/R-HSA-12... \n", - "\n", - " 3 4 5 \n", - "2095 Hemostasis TAS Homo sapiens \n", - "2096 Adaptive Immune System TAS Homo sapiens \n", - "2097 Adaptive Immune System IEA Homo sapiens " - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# download data\n", "url = 'https://reactome.org/download/current/UniProt2Reactome_All_Levels.txt'\n", @@ -9464,100 +6464,9 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>0</th>\n", - " <th>1</th>\n", - " <th>2</th>\n", - " <th>3</th>\n", - " <th>4</th>\n", - " <th>5</th>\n", - " <th>Uniprot_Accession_IDs</th>\n", - " <th>Protein_Ontology_IDs</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>A0A075B6P5</td>\n", - " <td>reactome_R-HSA-109582</td>\n", - " <td>https://reactome.org/PathwayBrowser/#/R-HSA-10...</td>\n", - " <td>Hemostasis</td>\n", - " <td>TAS</td>\n", - " <td>Homo sapiens</td>\n", - " <td>A0A075B6P5</td>\n", - " <td>PR_A0A075B6P5</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>A0A075B6P5</td>\n", - " <td>reactome_R-HSA-1280218</td>\n", - " <td>https://reactome.org/PathwayBrowser/#/R-HSA-12...</td>\n", - " <td>Adaptive Immune System</td>\n", - " <td>TAS</td>\n", - " <td>Homo sapiens</td>\n", - " <td>A0A075B6P5</td>\n", - " <td>PR_A0A075B6P5</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>A0A075B6P5</td>\n", - " <td>reactome_R-HSA-1280218</td>\n", - " <td>https://reactome.org/PathwayBrowser/#/R-HSA-12...</td>\n", - " <td>Adaptive Immune System</td>\n", - " <td>IEA</td>\n", - " <td>Homo sapiens</td>\n", - " <td>A0A075B6P5</td>\n", - " <td>PR_A0A075B6P5</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " 0 1 \\\n", - "0 A0A075B6P5 reactome_R-HSA-109582 \n", - "1 A0A075B6P5 reactome_R-HSA-1280218 \n", - "2 A0A075B6P5 reactome_R-HSA-1280218 \n", - "\n", - " 2 3 \\\n", - "0 https://reactome.org/PathwayBrowser/#/R-HSA-10... Hemostasis \n", - "1 https://reactome.org/PathwayBrowser/#/R-HSA-12... Adaptive Immune System \n", - "2 https://reactome.org/PathwayBrowser/#/R-HSA-12... Adaptive Immune System \n", - "\n", - " 4 5 Uniprot_Accession_IDs Protein_Ontology_IDs \n", - "0 TAS Homo sapiens A0A075B6P5 PR_A0A075B6P5 \n", - "1 TAS Homo sapiens A0A075B6P5 PR_A0A075B6P5 \n", - "2 IEA Homo sapiens A0A075B6P5 PR_A0A075B6P5 " - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "rce_prot_pth = rce_prot_pth.merge(uniprot_pro_map, left_on=0, right_on='Uniprot_Accession_IDs')\n", "\n", @@ -9574,31 +6483,9 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/131561 [00:00<?, ?it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "protein-pathway PR_A0A075B6P5-reactome_R-HSA-109582 reactome_R-HSA-109582\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'protein-pathway': {}})\n", "\n", @@ -9650,23 +6537,29 @@ "\n", "**Edges:** \n", "- `variant-disease` \n", - "- `variatn-phenotype` \n", + "- `variant-phenotype` \n", "\n", "**Identifier Maps:** \n", "- Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt)\n", "- Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) \n", "\n", "This chunk process the [`CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + " _Disease, Phenotype_ \n", " - `Phenotype`: A string containing a disease identifier and prefix. Sources are OMIM, MedGen (UMLS), and Orphanet. \n", + " \n", + " _Variant_ \n", " - `VariantName`: A string containing the name of the variant. \n", " - `rs_id`: An integer that represents a dbSNP identifier. \n", " - `AlleleID`: An integer that represents an Allele identifier. \n", " - `RCVaccession`: An integer that represents an RCV accession identifier. \n", " - `Type`: Character, the type of variant represented by the AlleleID. \n", " - `Assembly`: A list of dictionaries, stored as a string, that contains information related to the assembly (i.e., Assembly, ChromosomeAccession, Chromosome, Start, Stop, ReferenceAlel, AlernateAllel, Cytogenetic, and PositionVCF). \n", + "\n", + "\n", "- **Edges:** \n", " - `OtherIDs`: A \"|\"-delimited list of other identifiers associated with the variant edge. Note that each identifier included also includes a prefix. \n", + " - `GeneID`: An identifier for the gene associated with each variant (wherever possible). \n", " - `Guidelines`: Character, ACMG only right now. \n", " - `TestedInGTR`: Character, Y/N for Yes/No if there is a test registered as specific to this variant in the NIH Genetic Testing Registry (GTR). \n", " - `LastEvaluated`: Date, the latest date any submitter reported clinical significance. \n", @@ -9685,7 +6578,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -9707,190 +6600,9 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>VariationID</th>\n", - " <th>AlleleID</th>\n", - " <th>RS# (dbSNP)</th>\n", - " <th>Type</th>\n", - " <th>VariantName</th>\n", - " <th>RCVaccession</th>\n", - " <th>LastEvaluated</th>\n", - " <th>ReviewStatus</th>\n", - " <th>ClinicalSignificance</th>\n", - " <th>ClinSigSimple</th>\n", - " <th>...</th>\n", - " <th>Origin</th>\n", - " <th>OriginSimple</th>\n", - " <th>Assembly</th>\n", - " <th>Phenotype</th>\n", - " <th>Citation</th>\n", - " <th>OtherIDs</th>\n", - " <th>Disease_IDs_x</th>\n", - " <th>MONDO_IDs</th>\n", - " <th>Disease_IDs_y</th>\n", - " <th>HP_IDs</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>clinvar_9</td>\n", - " <td>15048</td>\n", - " <td>1800562.0</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_000410.4(HFE):c.845G&gt;A (p.Cys282Tyr)</td>\n", - " <td>RCV000000023|RCV000000025|RCV000000019|RCV0000...</td>\n", - " <td>September 15, 2021</td>\n", - " <td>criteria provided, conflicting interpretations</td>\n", - " <td>Conflicting interpretations of pathogenicity, ...</td>\n", - " <td>1</td>\n", - " <td>...</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0004975</td>\n", - " <td>NCBIBookShelf:NBK1440|PubMed:10401000|PubMed:1...</td>\n", - " <td>UniProtKB:Q30201#VAR_004398|OMIM:613609.0001|C...</td>\n", - " <td>MONDO:0004975</td>\n", - " <td>MONDO_0004975</td>\n", - " <td>MONDO:0004975</td>\n", - " <td>HP_0002511</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>clinvar_8847</td>\n", - " <td>23886</td>\n", - " <td>63750110.0</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_000447.3(PSEN2):c.1316A&gt;C (p.Asp439Ala)</td>\n", - " <td>RCV000009395|RCV000084269|RCV000172102</td>\n", - " <td>June 24, 2013</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>Uncertain significance</td>\n", - " <td>1</td>\n", - " <td>...</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0004975</td>\n", - " <td>PubMed:23861362|PubMed:11723295</td>\n", - " <td>ClinGen:CA224963|OMIM:600759.0003</td>\n", - " <td>MONDO:0004975</td>\n", - " <td>MONDO_0004975</td>\n", - " <td>MONDO:0004975</td>\n", - " <td>HP_0002511</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>clinvar_8852</td>\n", - " <td>23891</td>\n", - " <td>63750197.0</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_000447.3(PSEN2):c.389C&gt;T (p.Ser130Leu)</td>\n", - " <td>RCV000009401|RCV000009400|RCV000084261|RCV0001...</td>\n", - " <td>December 30, 2020</td>\n", - " <td>criteria provided, multiple submitters, no con...</td>\n", - " <td>Benign/Likely benign</td>\n", - " <td>1</td>\n", - " <td>...</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>[{'Assembly': 'GRCh37', 'ChromosomeAccession':...</td>\n", - " <td>MONDO:0004975</td>\n", - " <td>PubMed:28492532|PubMed:30045758|PubMed:1462372...</td>\n", - " <td>ClinGen:CA224951|UniProtKB:P49810#VAR_064903|O...</td>\n", - " <td>MONDO:0004975</td>\n", - " <td>MONDO_0004975</td>\n", - " <td>MONDO:0004975</td>\n", - " <td>HP_0002511</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "<p>3 rows × 24 columns</p>\n", - "</div>" - ], - "text/plain": [ - " VariationID AlleleID RS# (dbSNP) Type \\\n", - "0 clinvar_9 15048 1800562.0 single nucleotide variant \n", - "1 clinvar_8847 23886 63750110.0 single nucleotide variant \n", - "2 clinvar_8852 23891 63750197.0 single nucleotide variant \n", - "\n", - " VariantName \\\n", - "0 NM_000410.4(HFE):c.845G>A (p.Cys282Tyr) \n", - "1 NM_000447.3(PSEN2):c.1316A>C (p.Asp439Ala) \n", - "2 NM_000447.3(PSEN2):c.389C>T (p.Ser130Leu) \n", - "\n", - " RCVaccession LastEvaluated \\\n", - "0 RCV000000023|RCV000000025|RCV000000019|RCV0000... September 15, 2021 \n", - "1 RCV000009395|RCV000084269|RCV000172102 June 24, 2013 \n", - "2 RCV000009401|RCV000009400|RCV000084261|RCV0001... December 30, 2020 \n", - "\n", - " ReviewStatus \\\n", - "0 criteria provided, conflicting interpretations \n", - "1 criteria provided, single submitter \n", - "2 criteria provided, multiple submitters, no con... \n", - "\n", - " ClinicalSignificance ClinSigSimple ... \\\n", - "0 Conflicting interpretations of pathogenicity, ... 1 ... \n", - "1 Uncertain significance 1 ... \n", - "2 Benign/Likely benign 1 ... \n", - "\n", - " Origin OriginSimple \\\n", - "0 germline;unknown germline \n", - "1 germline;unknown germline \n", - "2 germline;unknown germline \n", - "\n", - " Assembly Phenotype \\\n", - "0 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0004975 \n", - "1 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0004975 \n", - "2 [{'Assembly': 'GRCh37', 'ChromosomeAccession':... MONDO:0004975 \n", - "\n", - " Citation \\\n", - "0 NCBIBookShelf:NBK1440|PubMed:10401000|PubMed:1... \n", - "1 PubMed:23861362|PubMed:11723295 \n", - "2 PubMed:28492532|PubMed:30045758|PubMed:1462372... \n", - "\n", - " OtherIDs Disease_IDs_x \\\n", - "0 UniProtKB:Q30201#VAR_004398|OMIM:613609.0001|C... MONDO:0004975 \n", - "1 ClinGen:CA224963|OMIM:600759.0003 MONDO:0004975 \n", - "2 ClinGen:CA224951|UniProtKB:P49810#VAR_064903|O... MONDO:0004975 \n", - "\n", - " MONDO_IDs Disease_IDs_y HP_IDs \n", - "0 MONDO_0004975 MONDO:0004975 HP_0002511 \n", - "1 MONDO_0004975 MONDO:0004975 HP_0002511 \n", - "2 MONDO_0004975 MONDO:0004975 HP_0002511 \n", - "\n", - "[3 rows x 24 columns]" - ] - }, - "execution_count": 74, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "clv_var_dis = clv_var_dis.merge(disease_maps, left_on='Phenotype', right_on='Disease_IDs')\n", "clv_var_dis = clv_var_dis.merge(phenotype_maps, left_on='Phenotype', right_on='Disease_IDs')\n", @@ -9908,31 +6620,9 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/17500 [00:00<?, ?it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "variant-phenotype clinvar_9-HP_0002511 clinvar_9\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'variant-disease': {}, 'variant-phenotype': {}})\n", "\n", @@ -9941,7 +6631,8 @@ " node_key = row['VariationID']; rcv = row['RCVaccession']; var_type = row['Type']\n", " pheno = row['Phenotype']; rs_id = row['RS# (dbSNP)']; allele_id = row['AlleleID']\n", " assembly = row['Assembly']; var_name = row['VariantName']\n", - " evidence = [{'ClinVar_OtherIDs': row['OtherIDs'],\n", + " evidence = [{'ClinVar_GeneID': row['GeneID'],\n", + " 'ClinVar_OtherIDs': row['OtherIDs'],\n", " 'ClinVar_Guidelines': row['Guidelines'],\n", " 'ClinVar_TestedInGTR': row['TestedInGTR'],\n", " 'ClinVar_LastEvaluated': row['LastEvaluated'],\n", @@ -9975,8 +6666,8 @@ " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_Assembly'] |= {assembly}\n", " else:\n", " master_metadata_dictionary['nodes'][node_key].update({\n", - " url: {'ClinVar_rs_id': {var_name},\n", - " 'ClinVar_VariantName': {rs_id},\n", + " url: {'ClinVar_rs_id': {rs_id},\n", + " 'ClinVar_VariantName': {var_name},\n", " 'ClinVar_AlleleID': {allele_id},\n", " 'ClinVar_RCVaccession': {rcv},\n", " 'ClinVar_Type': {var_type},\n", @@ -9984,8 +6675,8 @@ " }})\n", " else:\n", " master_metadata_dictionary['nodes'].update({node_key: {\n", - " url: {'ClinVar_rs_id': {var_name},\n", - " 'ClinVar_rs_id': {rs_id},\n", + " url: {'ClinVar_rs_id': {rs_id},\n", + " 'ClinVar_VariantName': {var_name},\n", " 'ClinVar_AlleleID': {allele_id},\n", " 'ClinVar_RCVaccession': {rcv},\n", " 'ClinVar_Type': {var_type},\n", @@ -10014,7 +6705,8 @@ "- `variant-gene` \n", "\n", "This chunk process the [`CLINVAR_VARIANT_GENE_EDGES.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_EDGES.txt) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + " _Variant_ \n", " - `VariantName`: A string containing the name of the variant. \n", " - `rs_id`: An integer that represents a dbSNP identifier. \n", " - `AlleleID`: An integer that represents an Allele identifier. \n", @@ -10029,6 +6721,11 @@ " -Near gene, upstream: Outside the location of the gene on the genome, within 5 kb\n", " - Within multiple genes by overlap: The variant is within genes that overlap on the genome. Includes introns\n", " - Within single gene: The variant is in only one gene. Includes introns\n", + " \n", + " _Gene_ \n", + " - `GenomicInformation`: A dictionary of gene identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", + "\n", + "\n", "- **Edges:** \n", " - `OtherIDs`: A \"|\"-delimited list of other identifiers associated with the variant edge. Note that each identifier included also includes a prefix. \n", " - `Guidelines`: Character, ACMG only right now. \n", @@ -10049,180 +6746,9 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>VariationID</th>\n", - " <th>AlleleID</th>\n", - " <th>RS# (dbSNP)</th>\n", - " <th>Type</th>\n", - " <th>VariantName</th>\n", - " <th>OtherIDs</th>\n", - " <th>GeneID</th>\n", - " <th>GeneSymbol</th>\n", - " <th>GeneName</th>\n", - " <th>GenesPerAlleleID</th>\n", - " <th>...</th>\n", - " <th>LastEvaluated</th>\n", - " <th>ReviewStatus</th>\n", - " <th>ClinicalSignificance</th>\n", - " <th>ClinSigSimple</th>\n", - " <th>Origin</th>\n", - " <th>OriginSimple</th>\n", - " <th>Source</th>\n", - " <th>SubmitterCategories</th>\n", - " <th>NumberSubmitters</th>\n", - " <th>Citation</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>clinvar_2</td>\n", - " <td>15041</td>\n", - " <td>397704705.0</td>\n", - " <td>Indel</td>\n", - " <td>NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA...</td>\n", - " <td>ClinGen:CA215070|OMIM:613653.0001</td>\n", - " <td>NCBIGene_9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", - " <td>1.0</td>\n", - " <td>...</td>\n", - " <td>NaN</td>\n", - " <td>criteria provided, single submitter</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>germline;unknown</td>\n", - " <td>germline</td>\n", - " <td>submitted</td>\n", - " <td>3</td>\n", - " <td>2</td>\n", - " <td>PubMed:20613862|PubMed:25741868|PubMedCentral:...</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>clinvar_3</td>\n", - " <td>15042</td>\n", - " <td>397704709.0</td>\n", - " <td>Deletion</td>\n", - " <td>NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs)</td>\n", - " <td>ClinGen:CA215072|OMIM:613653.0002</td>\n", - " <td>NCBIGene_9907</td>\n", - " <td>AP5Z1</td>\n", - " <td>adaptor related protein complex 5 subunit zeta 1</td>\n", - " <td>1.0</td>\n", - " <td>...</td>\n", - " <td>June 29, 2010</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>Pathogenic</td>\n", - " <td>1</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>submitted</td>\n", - " <td>1</td>\n", - " <td>1</td>\n", - " <td>PubMed:20613862</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>clinvar_4</td>\n", - " <td>15043</td>\n", - " <td>150829393.0</td>\n", - " <td>single nucleotide variant</td>\n", - " <td>NM_014630.3(ZNF592):c.3136G&gt;A (p.Gly1046Arg)</td>\n", - " <td>ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O...</td>\n", - " <td>NCBIGene_9640</td>\n", - " <td>ZNF592</td>\n", - " <td>zinc finger protein 592</td>\n", - " <td>1.0</td>\n", - " <td>...</td>\n", - " <td>June 29, 2015</td>\n", - " <td>no assertion criteria provided</td>\n", - " <td>Uncertain significance</td>\n", - " <td>0</td>\n", - " <td>germline</td>\n", - " <td>germline</td>\n", - " <td>submitted</td>\n", - " <td>1</td>\n", - " <td>1</td>\n", - " <td>PubMed:26123727|PubMed:12030328|PubMed:20531441</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "<p>3 rows × 25 columns</p>\n", - "</div>" - ], - "text/plain": [ - " VariationID AlleleID RS# (dbSNP) Type \\\n", - "0 clinvar_2 15041 397704705.0 Indel \n", - "1 clinvar_3 15042 397704709.0 Deletion \n", - "2 clinvar_4 15043 150829393.0 single nucleotide variant \n", - "\n", - " VariantName \\\n", - "0 NM_014855.3(AP5Z1):c.80_83delinsTGCTGTAAACTGTA... \n", - "1 NM_014855.3(AP5Z1):c.1413_1426del (p.Leu473fs) \n", - "2 NM_014630.3(ZNF592):c.3136G>A (p.Gly1046Arg) \n", - "\n", - " OtherIDs GeneID \\\n", - "0 ClinGen:CA215070|OMIM:613653.0001 NCBIGene_9907 \n", - "1 ClinGen:CA215072|OMIM:613653.0002 NCBIGene_9907 \n", - "2 ClinGen:CA210674|UniProtKB:Q92610#VAR_064583|O... NCBIGene_9640 \n", - "\n", - " GeneSymbol GeneName \\\n", - "0 AP5Z1 adaptor related protein complex 5 subunit zeta 1 \n", - "1 AP5Z1 adaptor related protein complex 5 subunit zeta 1 \n", - "2 ZNF592 zinc finger protein 592 \n", - "\n", - " GenesPerAlleleID ... LastEvaluated ReviewStatus \\\n", - "0 1.0 ... NaN criteria provided, single submitter \n", - "1 1.0 ... June 29, 2010 no assertion criteria provided \n", - "2 1.0 ... June 29, 2015 no assertion criteria provided \n", - "\n", - " ClinicalSignificance ClinSigSimple Origin OriginSimple \\\n", - "0 Pathogenic 1 germline;unknown germline \n", - "1 Pathogenic 1 germline germline \n", - "2 Uncertain significance 0 germline germline \n", - "\n", - " Source SubmitterCategories NumberSubmitters \\\n", - "0 submitted 3 2 \n", - "1 submitted 1 1 \n", - "2 submitted 1 1 \n", - "\n", - " Citation \n", - "0 PubMed:20613862|PubMed:25741868|PubMedCentral:... \n", - "1 PubMed:20613862 \n", - "2 PubMed:26123727|PubMed:12030328|PubMed:20531441 \n", - "\n", - "[3 rows x 25 columns]" - ] - }, - "execution_count": 84, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# download data\n", "url = 'https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/CLINVAR_VARIANT_GENE_EDGES.txt'\n", @@ -10245,31 +6771,9 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/1161070 [00:00<?, ?it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "variant-gene clinvar_2-NCBIGene_9907 clinvar_2\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'variant-gene': {}})\n", "\n", @@ -10325,8 +6829,8 @@ " master_metadata_dictionary['nodes'][node_key][url]['ClinVar_Category'] |= {category}\n", " else:\n", " master_metadata_dictionary['nodes'][node_key].update({\n", - " url: {'ClinVar_rs_id': {var_name},\n", - " 'ClinVar_VariantName': {rs_id},\n", + " url: {'ClinVar_rs_id': {rs_id},\n", + " 'ClinVar_VariantName': {var_name},\n", " 'ClinVar_AlleleID': {allele_id},\n", " 'ClinVar_RCVaccession': {rcv},\n", " 'ClinVar_Type': {var_type},\n", @@ -10336,8 +6840,8 @@ " }})\n", " else:\n", " master_metadata_dictionary['nodes'].update({node_key: {\n", - " url: {'ClinVar_rs_id': {var_name},\n", - " 'ClinVar_rs_id': {rs_id},\n", + " url: {'ClinVar_rs_id': {rs_id},\n", + " 'ClinVar_VariantName': {var_name},\n", " 'ClinVar_AlleleID': {allele_id},\n", " 'ClinVar_RCVaccession': {rcv},\n", " 'ClinVar_Type': {var_type},\n", @@ -10377,10 +6881,16 @@ "- RNA: [GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt) \n", "\n", "This chunk process the [`HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + " _Anatomy, Cell_ \n", " - `Anatomy`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. \n", " - `Anatomy_Type`: A string indicating the type of annotation. \n", - " - `Subcellular_Location`: A string containing a subcellular compartment. \n", + " - `Subcellular_Location`: A string containing a subcellular compartment. \n", + " \n", + " _Protein, RNA_ \n", + " - `GenomicInformation`: A dictionary of protein identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", + "\n", + "\n", "- **Edges:** \n", " - `Expression_Value`: The expression value derived from the experiments. \n", " - `Source`: A string indicating the source of the data (i.e., Human Protein Atlas or the Genotype-Tissue Expression project). \n", @@ -10389,7 +6899,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -10414,154 +6924,9 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>0</th>\n", - " <th>1</th>\n", - " <th>2</th>\n", - " <th>3</th>\n", - " <th>4</th>\n", - " <th>5</th>\n", - " <th>6</th>\n", - " <th>7</th>\n", - " <th>8</th>\n", - " <th>Uniprot_Accession_IDs</th>\n", - " <th>Protein_Ontology_IDs</th>\n", - " <th>anatomy_ids</th>\n", - " <th>ontolgoy_ids</th>\n", - " <th>Gene_Symbols</th>\n", - " <th>Ensembl_Transcript_IDs</th>\n", - " <th>Gene_Type</th>\n", - " <th>Ensembl_Transcript_Type</th>\n", - " <th>Master_Gene_Type</th>\n", - " <th>Master_Transcript_Type</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>ENSG00000121410</td>\n", - " <td>A1BG</td>\n", - " <td>P04217</td>\n", - " <td>Evidence at protein level</td>\n", - " <td>anatomy</td>\n", - " <td>None</td>\n", - " <td>liver</td>\n", - " <td>1234.7</td>\n", - " <td>The Human Protein Atlas</td>\n", - " <td>P04217</td>\n", - " <td>PR_P04217</td>\n", - " <td>liver</td>\n", - " <td>UBERON_0001114</td>\n", - " <td>A1BG</td>\n", - " <td>ensembl_ENST00000595014</td>\n", - " <td>protein-coding</td>\n", - " <td>retained_intron</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>ENSG00000121410</td>\n", - " <td>A1BG</td>\n", - " <td>P04217</td>\n", - " <td>Evidence at protein level</td>\n", - " <td>anatomy</td>\n", - " <td>None</td>\n", - " <td>liver</td>\n", - " <td>1234.7</td>\n", - " <td>The Human Protein Atlas</td>\n", - " <td>P04217</td>\n", - " <td>PR_P04217</td>\n", - " <td>liver</td>\n", - " <td>UBERON_0001114</td>\n", - " <td>A1BG</td>\n", - " <td>ensembl_ENST00000596924</td>\n", - " <td>protein-coding</td>\n", - " <td>processed_transcript</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>ENSG00000121410</td>\n", - " <td>A1BG</td>\n", - " <td>P04217</td>\n", - " <td>Evidence at protein level</td>\n", - " <td>anatomy</td>\n", - " <td>None</td>\n", - " <td>liver</td>\n", - " <td>1234.7</td>\n", - " <td>The Human Protein Atlas</td>\n", - " <td>P04217</td>\n", - " <td>PR_P04217</td>\n", - " <td>liver</td>\n", - " <td>UBERON_0001114</td>\n", - " <td>A1BG</td>\n", - " <td>ensembl_ENST00000263100</td>\n", - " <td>protein-coding</td>\n", - " <td>protein_coding</td>\n", - " <td>protein-coding</td>\n", - " <td>protein-coding</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " 0 1 2 3 4 5 \\\n", - "0 ENSG00000121410 A1BG P04217 Evidence at protein level anatomy None \n", - "1 ENSG00000121410 A1BG P04217 Evidence at protein level anatomy None \n", - "2 ENSG00000121410 A1BG P04217 Evidence at protein level anatomy None \n", - "\n", - " 6 7 8 Uniprot_Accession_IDs \\\n", - "0 liver 1234.7 The Human Protein Atlas P04217 \n", - "1 liver 1234.7 The Human Protein Atlas P04217 \n", - "2 liver 1234.7 The Human Protein Atlas P04217 \n", - "\n", - " Protein_Ontology_IDs anatomy_ids ontolgoy_ids Gene_Symbols \\\n", - "0 PR_P04217 liver UBERON_0001114 A1BG \n", - "1 PR_P04217 liver UBERON_0001114 A1BG \n", - "2 PR_P04217 liver UBERON_0001114 A1BG \n", - "\n", - " Ensembl_Transcript_IDs Gene_Type Ensembl_Transcript_Type \\\n", - "0 ensembl_ENST00000595014 protein-coding retained_intron \n", - "1 ensembl_ENST00000596924 protein-coding processed_transcript \n", - "2 ensembl_ENST00000263100 protein-coding protein_coding \n", - "\n", - " Master_Gene_Type Master_Transcript_Type \n", - "0 protein-coding protein-coding \n", - "1 protein-coding protein-coding \n", - "2 protein-coding protein-coding " - ] - }, - "execution_count": 109, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "hpa_gen_ant = hpa_gen_ant.merge(uniprot_pro_map, left_on=2, right_on='Uniprot_Accession_IDs')\n", "hpa_gen_ant = hpa_gen_ant.merge(anatomy_maps, left_on=6, right_on='anatomy_ids')\n", @@ -10580,31 +6945,9 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/823001 [00:07<?, ?it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "protein-anatomy PR_P04217-UBERON_0001114 UBERON_0001114\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'protein-anatomy': {}, 'protein-cell': {}, 'rna-anatomy': {}, 'rna-cell': {}})\n", "\n", @@ -10676,84 +7019,20 @@ "\n", "This chunk process the [`UNIPROT_PROTEIN_CATALYST.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_CATALYST.txt) file and obtains the following node and edge metadata: \n", " \n", + "- **Nodes:** \n", + " _Protein_ \n", + " - `GenomicInformation`: A dictionary of protein identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", + "\n", + "\n", "- **Edges:** \n", " - `Status`: A string to indicate the status of the entry in Uniprot. " ] }, { "cell_type": "code", - "execution_count": 117, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>0</th>\n", - " <th>1</th>\n", - " <th>2</th>\n", - " <th>3</th>\n", - " <th>4</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>PR_Q9NY84</td>\n", - " <td>CHEBI_16753</td>\n", - " <td>reviewed</td>\n", - " <td>Q9NY84</td>\n", - " <td>VNN3_HUMAN</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>PR_Q9NY84</td>\n", - " <td>CHEBI_15377</td>\n", - " <td>reviewed</td>\n", - " <td>Q9NY84</td>\n", - " <td>VNN3_HUMAN</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>PR_Q9NY84</td>\n", - " <td>CHEBI_29032</td>\n", - " <td>reviewed</td>\n", - " <td>Q9NY84</td>\n", - " <td>VNN3_HUMAN</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " 0 1 2 3 4\n", - "0 PR_Q9NY84 CHEBI_16753 reviewed Q9NY84 VNN3_HUMAN\n", - "1 PR_Q9NY84 CHEBI_15377 reviewed Q9NY84 VNN3_HUMAN\n", - "2 PR_Q9NY84 CHEBI_29032 reviewed Q9NY84 VNN3_HUMAN" - ] - }, - "execution_count": 117, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# download data\n", "url = 'https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_CATALYST.txt'\n", @@ -10776,31 +7055,9 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/95058 [00:00<?, ?it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "protein-catalyst PR_Q9NY84-CHEBI_16753 PR_Q9NY84\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'protein-catalyst': {}})\n", "\n", @@ -10851,84 +7108,20 @@ "\n", "This chunk process the [`UNIPROT_PROTEIN_COFACTOR.txt`](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_COFACTOR.txt) file and obtains the following node and edge metadata: \n", " \n", + "- **Nodes:** \n", + " _Protein_ \n", + " - `GenomicInformation`: A dictionary of protein identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", + "\n", + "\n", "- **Edges:** \n", " - `Status`: A string to indicate the status of the entry in Uniprot. " ] }, { "cell_type": "code", - "execution_count": 125, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>0</th>\n", - " <th>1</th>\n", - " <th>2</th>\n", - " <th>3</th>\n", - " <th>4</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>PR_Q5D1E8</td>\n", - " <td>CHEBI_18420</td>\n", - " <td>reviewed</td>\n", - " <td>Q5D1E8</td>\n", - " <td>ZC12A_HUMAN</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>PR_Q9NQH7</td>\n", - " <td>CHEBI_29035</td>\n", - " <td>reviewed</td>\n", - " <td>Q9NQH7</td>\n", - " <td>XPP3_HUMAN</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>PR_Q96TA2</td>\n", - " <td>CHEBI_29105</td>\n", - " <td>reviewed</td>\n", - " <td>Q96TA2</td>\n", - " <td>YMEL1_HUMAN</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " 0 1 2 3 4\n", - "0 PR_Q5D1E8 CHEBI_18420 reviewed Q5D1E8 ZC12A_HUMAN\n", - "1 PR_Q9NQH7 CHEBI_29035 reviewed Q9NQH7 XPP3_HUMAN\n", - "2 PR_Q96TA2 CHEBI_29105 reviewed Q96TA2 YMEL1_HUMAN" - ] - }, - "execution_count": 125, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# download data\n", "url = 'https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_PROTEIN_COFACTOR.txt'\n", @@ -10951,38 +7144,16 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/10630 [00:00<?, ?it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "protein-catalyst PR_Q5D1E8-CHEBI_18420 PR_Q5D1E8\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'protein-cofactor': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(upt_prot_cof.iterrows(), total=upt_prot_cof.shape[0]):\n", " node_key = row[0]; chebi = row[1]; evidence = [{'Uniprot_Status': row[2]}] \n", - " edge_key = '{}-{}'.format(node_key, chebi); edge_type = 'protein-catalyst'\n", + " edge_key = '{}-{}'.format(node_key, chebi); edge_type = 'protein-cofactor'\n", " \n", " # add catalyst information\n", " if chebi in master_metadata_dictionary['nodes'].keys():\n", @@ -11029,76 +7200,20 @@ "\n", "This chunk process the [`9606.protein.links.v11.0.txt.gz`](https://stringdb-static.org/download/protein.links.v11.0/9606.protein.links.v11.0.txt.gz) file and obtains the following node and edge metadata: \n", " \n", + "- **Nodes:** \n", + " _Protein_ \n", + " - `GenomicInformation`: A dictionary of protein identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", + "\n", + "\n", "- **Edges:** \n", " - `combined_score`: The combined score is computed by combining the probabilities from the different evidence channels and corrected for the probability of randomly observing an interaction. Scores range from 0-1000. For a more detailed description please see PMID:15608232. " ] }, { "cell_type": "code", - "execution_count": 131, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>protein1</th>\n", - " <th>protein2</th>\n", - " <th>combined_score</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>9606.ENSP00000000233</td>\n", - " <td>9606.ENSP00000272298</td>\n", - " <td>490</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>9606.ENSP00000000233</td>\n", - " <td>9606.ENSP00000253401</td>\n", - " <td>198</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>9606.ENSP00000000233</td>\n", - " <td>9606.ENSP00000401445</td>\n", - " <td>159</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " protein1 protein2 combined_score\n", - "0 9606.ENSP00000000233 9606.ENSP00000272298 490\n", - "1 9606.ENSP00000000233 9606.ENSP00000253401 198\n", - "2 9606.ENSP00000000233 9606.ENSP00000401445 159" - ] - }, - "execution_count": 131, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# download data\n", "url = 'https://stringdb-static.org/download/protein.links.v11.0/9606.protein.links.v11.0.txt.gz'\n", @@ -11118,96 +7233,9 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>protein1</th>\n", - " <th>protein2</th>\n", - " <th>combined_score</th>\n", - " <th>STRING_IDs_x</th>\n", - " <th>Protein_Ontology_IDs_x</th>\n", - " <th>STRING_IDs_y</th>\n", - " <th>Protein_Ontology_IDs_y</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>9606.ENSP00000000233</td>\n", - " <td>9606.ENSP00000272298</td>\n", - " <td>490</td>\n", - " <td>9606.ENSP00000000233</td>\n", - " <td>PR_P84085</td>\n", - " <td>9606.ENSP00000272298</td>\n", - " <td>PR_P0DP24</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>9606.ENSP00000001008</td>\n", - " <td>9606.ENSP00000272298</td>\n", - " <td>196</td>\n", - " <td>9606.ENSP00000001008</td>\n", - " <td>PR_Q02790</td>\n", - " <td>9606.ENSP00000272298</td>\n", - " <td>PR_P0DP24</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>9606.ENSP00000005178</td>\n", - " <td>9606.ENSP00000272298</td>\n", - " <td>155</td>\n", - " <td>9606.ENSP00000005178</td>\n", - " <td>PR_Q16654</td>\n", - " <td>9606.ENSP00000272298</td>\n", - " <td>PR_P0DP24</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " protein1 protein2 combined_score \\\n", - "0 9606.ENSP00000000233 9606.ENSP00000272298 490 \n", - "1 9606.ENSP00000001008 9606.ENSP00000272298 196 \n", - "2 9606.ENSP00000005178 9606.ENSP00000272298 155 \n", - "\n", - " STRING_IDs_x Protein_Ontology_IDs_x STRING_IDs_y \\\n", - "0 9606.ENSP00000000233 PR_P84085 9606.ENSP00000272298 \n", - "1 9606.ENSP00000001008 PR_Q02790 9606.ENSP00000272298 \n", - "2 9606.ENSP00000005178 PR_Q16654 9606.ENSP00000272298 \n", - "\n", - " Protein_Ontology_IDs_y \n", - "0 PR_P0DP24 \n", - "1 PR_P0DP24 \n", - "2 PR_P0DP24 " - ] - }, - "execution_count": 133, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "stg_prot_prot = stg_prot_prot.merge(string_pro_map, left_on='protein1', right_on='STRING_IDs')\n", "stg_prot_prot = stg_prot_prot.merge(string_pro_map, left_on='protein2', right_on='STRING_IDs')\n", @@ -11225,31 +7253,9 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/8334872 [00:01<?, ?it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "protein-protein PR_P84085-PR_P0DP24 PR_P0DP24\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'protein-protein': {}})\n", "\n", @@ -11298,11 +7304,17 @@ "- Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) \n", "\n", "This chunk process the [curated_gene_disease_associations.tsv](https://www.disgenet.org/static/disgenet_ap1/files/downloads/curated_gene_disease_associations.tsv.gz) file and obtains the following node and edge metadata: \n", - "- **Nodes:** \n", + "- **Nodes:** \n", + " _Disease, Phenotype_ \n", " - `diseaseId`: A string containing the concept's database cross-reference, which is formatted as DB:ID. If not, a MeSH or OMIM identifier. Variable is provided as a string with the \"MESH\" or \"OMIM\" prefix in all caps. \n", " - `diseaseName`: A string containing the concept's synonym. If derived from an ontology, the string will be prefixed by the synonym type. \n", " - `diseaseSematicType`: A string containing a high-level grouper or typing variable for the disease. \n", " - `diseaseClass`: A \";\"-delimnited list of ICD codes that can be used to classify the disease. \n", + " \n", + " _Gene_ \n", + " - `GenomicInformation`: A dictionary of gene identifier information. See the [Genomic Entity Metadata](#genomicinfo) code chunk for more details. \n", + "\n", + "\n", "- **Edges:** \n", " - `DSI`: The Disease Similarity Index ranges from from 0.25 to 1. It is calculated as: DSI = log2(# diseases assoc with gene/total # of diseases in DisGeNET) / log2(1/total # of diseases in DisGeNET) \n", " - `DPI`: The Disease Pleiotropy Index ranges from 0 to 1. it is calculated as: DPI = (# of MeSH disease classes of disease assoc with gene/total # of MeSH disease classes)*100. \n", @@ -11317,7 +7329,7 @@ }, { "cell_type": "code", - "execution_count": 154, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -11331,8 +7343,8 @@ "dgt_dis_gene = dgt_dis_gene[dgt_dis_gene['diseaseType'] != 'group']\n", "\n", "# fix variable typing\n", - "dgt_dis_gene['YearInitial'] = dgt_dis_gene['YearInitial'].astype('Int64')\n", - "dgt_dis_gene['YearFinal'] = dgt_dis_gene['YearFinal'].astype('Int64')\n", + "dgt_dis_gene['YearInitial'] = dgt_dis_gene['YearInitial'].astype('float').astype('Int64')\n", + "dgt_dis_gene['YearFinal'] = dgt_dis_gene['YearFinal'].astype('float').astype('Int64')\n", "\n", "# fix prefix\n", "dgt_dis_gene['geneId'] = 'NCBIGene_' + dgt_dis_gene['geneId'].astype('str')\n" @@ -11347,153 +7359,9 @@ }, { "cell_type": "code", - "execution_count": 155, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>geneId</th>\n", - " <th>geneSymbol</th>\n", - " <th>DSI</th>\n", - " <th>DPI</th>\n", - " <th>diseaseId</th>\n", - " <th>diseaseName</th>\n", - " <th>diseaseType</th>\n", - " <th>diseaseClass</th>\n", - " <th>diseaseSemanticType</th>\n", - " <th>score</th>\n", - " <th>EI</th>\n", - " <th>YearInitial</th>\n", - " <th>YearFinal</th>\n", - " <th>NofPmids</th>\n", - " <th>NofSnps</th>\n", - " <th>source</th>\n", - " <th>Disease_IDs_x</th>\n", - " <th>MONDO_IDs</th>\n", - " <th>Disease_IDs_y</th>\n", - " <th>HP_IDs</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>NCBIGene_2</td>\n", - " <td>A2M</td>\n", - " <td>0.529</td>\n", - " <td>0.769</td>\n", - " <td>C0002395</td>\n", - " <td>Alzheimer's Disease</td>\n", - " <td>disease</td>\n", - " <td>C10;F03</td>\n", - " <td>Disease or Syndrome</td>\n", - " <td>0.5</td>\n", - " <td>0.769</td>\n", - " <td>1998</td>\n", - " <td>2018</td>\n", - " <td>3</td>\n", - " <td>0</td>\n", - " <td>CTD_human</td>\n", - " <td>C0002395</td>\n", - " <td>MONDO_0004975</td>\n", - " <td>C0002395</td>\n", - " <td>HP_0002511</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>NCBIGene_43</td>\n", - " <td>ACHE</td>\n", - " <td>0.445</td>\n", - " <td>0.885</td>\n", - " <td>C0002395</td>\n", - " <td>Alzheimer's Disease</td>\n", - " <td>disease</td>\n", - " <td>C10;F03</td>\n", - " <td>Disease or Syndrome</td>\n", - " <td>0.4</td>\n", - " <td>0.985</td>\n", - " <td>1991</td>\n", - " <td>2020</td>\n", - " <td>2</td>\n", - " <td>0</td>\n", - " <td>CTD_human</td>\n", - " <td>C0002395</td>\n", - " <td>MONDO_0004975</td>\n", - " <td>C0002395</td>\n", - " <td>HP_0002511</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>NCBIGene_102</td>\n", - " <td>ADAM10</td>\n", - " <td>0.489</td>\n", - " <td>0.846</td>\n", - " <td>C0002395</td>\n", - " <td>Alzheimer's Disease</td>\n", - " <td>disease</td>\n", - " <td>C10;F03</td>\n", - " <td>Disease or Syndrome</td>\n", - " <td>0.7</td>\n", - " <td>0.986</td>\n", - " <td>2000</td>\n", - " <td>2019</td>\n", - " <td>1</td>\n", - " <td>1</td>\n", - " <td>CTD_human</td>\n", - " <td>C0002395</td>\n", - " <td>MONDO_0004975</td>\n", - " <td>C0002395</td>\n", - " <td>HP_0002511</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " geneId geneSymbol DSI DPI diseaseId diseaseName \\\n", - "0 NCBIGene_2 A2M 0.529 0.769 C0002395 Alzheimer's Disease \n", - "1 NCBIGene_43 ACHE 0.445 0.885 C0002395 Alzheimer's Disease \n", - "2 NCBIGene_102 ADAM10 0.489 0.846 C0002395 Alzheimer's Disease \n", - "\n", - " diseaseType diseaseClass diseaseSemanticType score EI YearInitial \\\n", - "0 disease C10;F03 Disease or Syndrome 0.5 0.769 1998 \n", - "1 disease C10;F03 Disease or Syndrome 0.4 0.985 1991 \n", - "2 disease C10;F03 Disease or Syndrome 0.7 0.986 2000 \n", - "\n", - " YearFinal NofPmids NofSnps source Disease_IDs_x MONDO_IDs \\\n", - "0 2018 3 0 CTD_human C0002395 MONDO_0004975 \n", - "1 2020 2 0 CTD_human C0002395 MONDO_0004975 \n", - "2 2019 1 1 CTD_human C0002395 MONDO_0004975 \n", - "\n", - " Disease_IDs_y HP_IDs \n", - "0 C0002395 HP_0002511 \n", - "1 C0002395 HP_0002511 \n", - "2 C0002395 HP_0002511 " - ] - }, - "execution_count": 155, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "dgt_dis_gene = dgt_dis_gene.merge(disease_maps, left_on='diseaseId', right_on='Disease_IDs')\n", "dgt_dis_gene = dgt_dis_gene.merge(phenotype_maps, left_on='diseaseId', right_on='Disease_IDs')\n", @@ -11511,31 +7379,9 @@ }, { "cell_type": "code", - "execution_count": 156, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/13011 [00:00<?, ?it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "gene-disease NCBIGene_2-MONDO_0004975 NCBIGene_2\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "master_metadata_dictionary['edges'].update({'gene-disease': {}, 'gene-phenotype': {}})\n", "\n", @@ -11543,12 +7389,12 @@ "for idx, row in tqdm(dgt_dis_gene.iterrows(), total=dgt_dis_gene.shape[0]):\n", " node_key = row['geneId']; dis_id = row['diseaseId']; dis_name = row['diseaseName']\n", " sem_type = row['diseaseSemanticType']; dis_cls = row['diseaseClass']\n", - " evidence = [{'DisGeNET_DSI': row['DSI'],\n", - " 'DisGeNET_DPI': row['DPI'],\n", - " 'DisGeNET_score': row['score'],\n", - " 'DisGeNET_EI': row['EI'],\n", - " 'DisGeNET_YearInitial': row['YearInitial'],\n", - " 'DisGeNET_YearFinal': row['YearFinal'],\n", + " evidence = [{'DisGeNET_DSI': row['DSI'] if not pandas.isna(row['DSI']) else 'None',\n", + " 'DisGeNET_DPI': row['DPI'] if not pandas.isna(row['DPI']) else 'None',\n", + " 'DisGeNET_score': row['score'] if not pandas.isna(row['score']) else 'None',\n", + " 'DisGeNET_EI': row['EI'] if not pandas.isna(row['EI']) else 'None',\n", + " 'DisGeNET_YearInitial': row['YearInitial'] if not pandas.isna(row['YearInitial']) else 'None',\n", + " 'DisGeNET_YearFinal': row['YearFinal'] if not pandas.isna(row['YearFinal']) else 'None',\n", " 'DisGeNET_NofPmids': row['NofPmids'],\n", " 'DisGeNET_NofSnps': row['NofSnps']}] \n", " if row['diseaseType'] == 'disease':\n", @@ -11612,7 +7458,7 @@ }, { "cell_type": "code", - "execution_count": 161, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -11629,6 +7475,20 @@ " f_out.write(bytes_out[idx:idx+max_bytes])" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(edge_type, edge_key, node_key)\n", + "break\n", + " \n", + "# master_metadata_dictionary['edges']['chemical-gobp']['CHEBI_44975-GO_0046031']\n", + "# master_metadata_dictionary['nodes']['GO_0046031']\n", + "master_metadata_dictionary['nodes']['CHEBI_44975']" + ] + }, { "cell_type": "markdown", "metadata": {}, From ad0fa93525d3a13469504240c8e35acaa086cf55 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:14:19 -0400 Subject: [PATCH 086/112] adding better tqdm --- notebooks/OWLNETS_Example_Application.ipynb | 2 +- notebooks/Ontology_Cleaning.ipynb | 2 +- notebooks/RDF_Graph_Processing_Example.ipynb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/notebooks/OWLNETS_Example_Application.ipynb b/notebooks/OWLNETS_Example_Application.ipynb index 9b40251f..dd7443f2 100644 --- a/notebooks/OWLNETS_Example_Application.ipynb +++ b/notebooks/OWLNETS_Example_Application.ipynb @@ -198,7 +198,7 @@ "from functools import reduce\n", "from rdflib import Graph, Namespace, URIRef, BNode, Literal\n", "from rdflib.namespace import OWL, RDF, RDFS\n", - "from tqdm import tqdm" + "from tqdm.notebook import tqdm" ] }, { diff --git a/notebooks/Ontology_Cleaning.ipynb b/notebooks/Ontology_Cleaning.ipynb index 79001acd..a93e6739 100644 --- a/notebooks/Ontology_Cleaning.ipynb +++ b/notebooks/Ontology_Cleaning.ipynb @@ -214,7 +214,7 @@ "import shutil\n", "\n", "from rdflib import Graph\n", - "from tqdm import tqdm\n", + "from tqdm.notebook import tqdm\n", "\n", "# import script containing helper functions\n", "from pkt_kg.utils import * \n", diff --git a/notebooks/RDF_Graph_Processing_Example.ipynb b/notebooks/RDF_Graph_Processing_Example.ipynb index aa699373..ccf94d84 100644 --- a/notebooks/RDF_Graph_Processing_Example.ipynb +++ b/notebooks/RDF_Graph_Processing_Example.ipynb @@ -88,7 +88,7 @@ "import os\n", "\n", "from rdflib import Graph, Namespace, URIRef, BNode, Literal\n", - "from tqdm import tqdm\n", + "from tqdm.notebook import tqdm\n", "\n", "from pkt_kg.utils import * # provides access to helper functions" ] From 6fc6a6efc5504f51bd4d7da08ac3a1f969b43634 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:14:53 -0400 Subject: [PATCH 087/112] adding ipywidgets --- notebooks/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/notebooks/requirements.txt b/notebooks/requirements.txt index 251df442..e430330e 100644 --- a/notebooks/requirements.txt +++ b/notebooks/requirements.txt @@ -1,4 +1,5 @@ Cython>=0.29.14 +ipywidgets>=7.7.0 more-itertools>=8.6.0 networkx>=2.4 numpy>=1.18.1 From 5be80375e20e0fc949a310e18f08818cc03eb22f Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:16:12 -0400 Subject: [PATCH 088/112] alphabetizing --- .gitignore | 1 + pkt_kg/utils/__init__.py | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 65ba6e7d..3f6b8f76 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ builds/temp/* #### External Libraries pkt_kg/libs/deepwalk_c_master/* pkt_kg/libs/walking-rdf-and-owl-master/* +pkt_kg/libs/pylucene* #### Scripts pkt_kg/kg_embedding_visualizer.py diff --git a/pkt_kg/utils/__init__.py b/pkt_kg/utils/__init__.py index 61d9f690..cdc827da 100644 --- a/pkt_kg/utils/__init__.py +++ b/pkt_kg/utils/__init__.py @@ -6,14 +6,15 @@ from .kg_utils import * -__all__ = ['url_download', 'ftp_url_download', 'gzipped_ftp_url_download', 'zipped_url_download', - 'gzipped_url_download', 'data_downloader', 'explodes_data', 'chunks', 'metadata_dictionary_mapper', - 'metadata_api_mapper', 'genomic_id_mapper', 'outputs_dictionary_data', 'obtains_entity_url', - 'gets_biolink_information', 'gets_ontology_statistics', 'gets_ontology_classes', 'load_jsonl', 'dump_jsonl', - 'gets_deprecated_ontology_classes', 'gets_object_properties', 'gets_ontology_class_dbxrefs', - 'gets_ontology_class_synonyms', 'merges_ontologies', 'ontology_file_formatter', 'adds_edges_to_graph', - 'remove_edges_from_graph', 'gets_entity_ancestors', 'connected_components', 'removes_self_loops', - 'derives_graph_statistics', 'splits_knowledge_graph', 'adds_namespace_to_bnodes', - 'removes_namespace_from_bnodes', 'updates_pkt_namespace_identifiers', 'finds_node_type', - 'updates_graph_namespace', 'maps_ids_to_integers', 'n3', 'appends_to_existing_file', - 'deduplicates_file', 'merges_files', 'convert_to_networkx', 'sublist_creator', 'gets_ontology_definitions'] +__all__ = ['adds_edges_to_graph', 'adds_namespace_to_bnodes', 'appends_to_existing_file', 'chunks', + 'connected_components', 'convert_to_networkx', 'data_downloader', 'deduplicates_file', + 'derives_graph_statistics', 'dump_jsonl', 'explodes_data', 'finds_node_type', 'ftp_url_download', + 'genomic_id_mapper', 'gets_biolink_information', 'gets_deprecated_ontology_classes', + 'gets_entity_ancestors', 'gets_object_properties', 'gets_ontology_class_dbxrefs', + 'gets_ontology_class_synonyms', 'gets_ontology_classes', 'gets_ontology_definitions', + 'gets_ontology_statistics', 'gzipped_ftp_url_download', 'gzipped_url_download', 'load_jsonl', + 'maps_ids_to_integers', 'merges_files', 'merges_ontologies', 'metadata_api_mapper', + 'metadata_dictionary_mapper', 'n3', 'obtains_entity_url', 'ontology_file_formatter', + 'outputs_dictionary_data', 'remove_edges_from_graph', 'removes_namespace_from_bnodes', 'removes_self_loops', + 'splits_knowledge_graph', 'sublist_creator', 'updates_graph_namespace', 'updates_pkt_namespace_identifiers', + 'url_download', 'zipped_url_download'] From e0f95f0ad4de8b89ec29401d46bb66c92b6c65a8 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:16:26 -0400 Subject: [PATCH 089/112] cleaning up linting errors --- pkt_kg/utils/data_utils.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index db202a30..b80e3680 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -506,14 +506,15 @@ def obtains_entity_url(prefix: str, identifier: Union[int, str], url: Optional[s url: A string containing a url. Returns: - entity_url: A string containing a valid BioRegistry URL (e.g., ). + entity_url: A string containing a valid BioRegistry URL. Raises: ValueError: If a JSONDecodeError is thrown, a ValueError is raised to alert the user that a bad identifier or prefix was provided. """ - prefix = prefix.lower(); identifier = str(identifier); res = None; entity_url = None + prefix = prefix.lower(); identifier = str(identifier) + entity_url: str = ''; res: Optional[Dict] = None obo_url = 'http://purl.obolibrary.org/obo/' obo_ont_prefixes = ['BFO', 'CHEBI', 'DOID', 'GO', 'OBI', 'PATO', 'PO', 'PR', 'XAO', 'ZFA', 'AEO', 'AGRO', 'AISM', 'AMPHX', 'APO', 'APOLLO_SV', 'ARO', 'BCO', 'BSPO', 'BTO', 'CARO', 'CDAO', 'CDNO', 'CHEMINF', @@ -540,7 +541,7 @@ def obtains_entity_url(prefix: str, identifier: Union[int, str], url: Optional[s if prefix.upper() in obo_ont_prefixes: entity_url = obo_url + prefix.upper() + '_' + identifier elif url is not None: entity_url = url else: raise ValueError('Error: Invalid prefix or identifier provided. Please check your input and try again.') - if not isinstance(entity_url, str): entity_url = res['providers']['bioregistry'] + if entity_url == '' and res is not None: entity_url = res['providers']['bioregistry'] return entity_url @@ -610,7 +611,12 @@ def dump_jsonl(data: List, output_path: str) -> None: with open(output_path, 'a+', encoding='utf-8') as f: for line in data: - json_record = json.dumps(line, ensure_ascii=False) + temp_dict = dict() + # check for nested dictionaries + for k, v in line.items(): + if isinstance(v, dict): temp_dict[k] = str(v) + else: temp_dict[k] = v + json_record = json.dumps(temp_dict, ensure_ascii=False) f.write(json_record + '\n') return None From fdac87445e86c94a03d009aaf088e319e6abe3cb Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:17:06 -0400 Subject: [PATCH 090/112] sprucing --- builds/data_preprocessing.py | 1013 +++++++++++++++++++++++++--------- 1 file changed, 757 insertions(+), 256 deletions(-) diff --git a/builds/data_preprocessing.py b/builds/data_preprocessing.py index 922dcd4c..0a73084c 100755 --- a/builds/data_preprocessing.py +++ b/builds/data_preprocessing.py @@ -5,6 +5,7 @@ # import fnmatch import glob # import itertools +import json import logging.config import networkx # type: ignore import numpy # type: ignore @@ -13,6 +14,7 @@ import pickle import re import requests +import shutil import sys from google.cloud import storage # type: ignore @@ -138,7 +140,7 @@ def _preprocess_hgnc_data(self) -> pandas.DataFrame: 'name', 'location', 'alias_name']] hgnc.rename(columns={'uniprot_ids': 'uniprot_id', 'location': 'map_location', 'locus_type': 'hgnc_gene_type'}, inplace=True) - hgnc['hgnc_id'].str.replace('.*\:', '', inplace=True, regex=True) # strip 'HGNC' off of the identifiers + hgnc['hgnc_id'] = hgnc['hgnc_id'].str.replace('.*\:', '', regex=True) # strip 'HGNC' off of the identifiers hgnc.fillna('None', inplace=True) # replace NaN with 'None' hgnc['entrez_id'] = hgnc['entrez_id'].apply(lambda x: str(int(x)) if x != 'None' else 'None') # make col str # combine certain columns into single column @@ -150,13 +152,13 @@ def _preprocess_hgnc_data(self) -> pandas.DataFrame: 'name', 'synonyms'], '|') # reformat hgnc gene type for v in self.genomic_type_mapper['hgnc_gene_type'].keys(): - explode_df_hgnc['hgnc_gene_type'].str.replace(v, self.genomic_type_mapper['hgnc_gene_type'][v], - inplace=True) + explode_df_hgnc['hgnc_gene_type'] = explode_df_hgnc['hgnc_gene_type'].str.replace( + v, self.genomic_type_mapper['hgnc_gene_type'][v]) # reformat master hgnc gene type explode_df_hgnc['master_gene_type'] = explode_df_hgnc['hgnc_gene_type'] master_dict = self.genomic_type_mapper['hgnc_master_gene_type'] for val in master_dict.keys(): - explode_df_hgnc['master_gene_type'].str.replace(val, master_dict[val], inplace=True) + explode_df_hgnc['master_gene_type'] = explode_df_hgnc['master_gene_type'].str.replace(val, master_dict[val]) # post-process reformatted data explode_df_hgnc.drop(['alias_symbol', 'alias_name'], axis=1, inplace=True) # remove original gene type column explode_df_hgnc.drop_duplicates(inplace=True) @@ -190,17 +192,21 @@ def _preprocess_ensembl_data(self) -> pandas.DataFrame: 'ensembl_gene_type', 'transcript_name', 'ensembl_transcript_type']) # reformat ensembl gene type gene_dict = self.genomic_type_mapper['ensembl_gene_type'] - for val in gene_dict.keys(): ensembl_geneset['ensembl_gene_type'].str.replace(val, gene_dict[val], inplace=True) + for val in gene_dict.keys(): + ensembl_geneset['ensembl_gene_type'] = ensembl_geneset['ensembl_gene_type'].str.replace(val, gene_dict[val]) # reformat master gene type ensembl_geneset['master_gene_type'] = ensembl_geneset['ensembl_gene_type'] gene_dict = self.genomic_type_mapper['ensembl_master_gene_type'] - for val in gene_dict.keys(): ensembl_geneset['master_gene_type'].str.replace(val, gene_dict[val], inplace=True) + for val in gene_dict.keys(): + ensembl_geneset['master_gene_type'] = ensembl_geneset['master_gene_type'].str.replace(val, gene_dict[val]) # reformat master transcript type - ensembl_geneset['ensembl_transcript_type'].str.replace('vault_RNA', 'vaultRNA', inplace=True, regex=False) + ensembl_geneset['ensembl_transcript_type'] = ensembl_geneset['ensembl_transcript_type'].str.replace( + 'vault_RNA', 'vaultRNA', regex=False) ensembl_geneset['master_transcript_type'] = ensembl_geneset['ensembl_transcript_type'] trans_d = self.genomic_type_mapper['ensembl_master_transcript_type'] for val in trans_d.keys(): - ensembl_geneset['master_transcript_type'].str.replace(val, trans_d[val], inplace=True) + ensembl_geneset['master_transcript_type'] = ensembl_geneset['master_transcript_type'].str.replace( + val, trans_d[val]) # post-process reformatted data ensembl_geneset.drop_duplicates(inplace=True) @@ -228,8 +234,6 @@ def merges_ensembl_mapping_data(self) -> pandas.DataFrame: ensembl_uniprot = ensembl_uniprot.loc[ensembl_uniprot['uniprot_id'].apply(lambda x: '-' not in x)] ensembl_uniprot = ensembl_uniprot.loc[ensembl_uniprot['info_type'].apply(lambda x: x == 'DIRECT')] ensembl_uniprot = ensembl_uniprot.loc[ensembl_uniprot['xref_identity'].apply(lambda x: x != 'None')] - # ensembl_uniprot['master_gene_type'] = ['protein-coding'] * len(ensembl_uniprot) - # ensembl_uniprot['master_transcript_type'] = ['protein-coding'] * len(ensembl_uniprot) ensembl_uniprot.drop(drop_cols, axis=1, inplace=True) ensembl_uniprot.drop_duplicates(subset=None, keep='first', inplace=True) # entrez data @@ -281,7 +285,8 @@ def _preprocess_uniprot_data(self) -> pandas.DataFrame: # explode nested data and perform light value reformatting explode_df_uniprot = explodes_data(uniprot.copy(), ['transcript_stable_id', 'entrez_id', 'hgnc_id'], ';') explode_df_uniprot = explodes_data(explode_df_uniprot.copy(), ['symbol', 'synonyms'], '|') - explode_df_uniprot['transcript_stable_id'].str.replace('\s.*', '', inplace=True, regex=True) # strip uniprot + explode_df_uniprot['transcript_stable_id'] = explode_df_uniprot['transcript_stable_id'].str.replace( + '\s.*', '', regex=True) # strip uniprot explode_df_uniprot.drop(['Status'], axis=1, inplace=True) explode_df_uniprot.drop_duplicates(inplace=True) @@ -326,12 +331,14 @@ def _preprocess_ncbi_data(self) -> pandas.DataFrame: explode_df_ncbi_gene['entrez_gene_type'] = explode_df_ncbi_gene['type_of_gene'] gene_dict = self.genomic_type_mapper['entrez_gene_type'] for val in gene_dict.keys(): - explode_df_ncbi_gene['entrez_gene_type'].str.replace(val, gene_dict[val], inplace=True) + explode_df_ncbi_gene['entrez_gene_type'] = explode_df_ncbi_gene['entrez_gene_type'].str.replace( + val, gene_dict[val]) # reformat master gene type explode_df_ncbi_gene['master_gene_type'] = explode_df_ncbi_gene['entrez_gene_type'] gene_dict = self.genomic_type_mapper['master_gene_type'] for val in gene_dict.keys(): - explode_df_ncbi_gene['master_gene_type'].str.replace(val, gene_dict[val], inplace=True) + explode_df_ncbi_gene['master_gene_type'] = explode_df_ncbi_gene['master_gene_type'].str.replace( + val, gene_dict[val]) # post-process reformatted data explode_df_ncbi_gene['hgnc_id'] = explode_df_ncbi_gene['hgnc_id'].str.replace('HGNC:', '', regex=True) explode_df_ncbi_gene['ensembl_gene_id'] = explode_df_ncbi_gene['ensembl_gene_id'].str.replace('Ensembl:', '', @@ -357,8 +364,8 @@ def _preprocess_protein_ontology_mapping_data(self) -> pandas.DataFrame: pro = self.reads_gcs_bucket_data_to_df(f_name='promapping.txt', delm='\t', head=col_names) pro = pro.loc[pro['Entry'].apply(lambda x: x.startswith('UniProtKB:') and '_VAR' not in x and ', ' not in x)] pro = pro.loc[pro['pro_mapping'].apply(lambda x: x.startswith('exact'))] - pro['pro_id'].str.replace('PR:', 'PR_', inplace=True, regex=True) # replace PR: with PR_ - pro['Entry'].str.replace('(^\w*\:)', '', inplace=True, regex=True) # remove ids which appear before ':' + pro['pro_id'] = pro['pro_id'].str.replace('PR:', 'PR_', regex=True) # replace PR: with PR_ + pro['Entry'] = pro['Entry'].str.replace('(^\w*\:)', '', regex=True) # remove ids which appear before ':' pro = pro.loc[pro['pro_id'].apply(lambda x: '-' not in x)] # remove isoforms pro.rename(columns={'Entry': 'uniprot_id'}, inplace=True) pro.drop(['pro_mapping'], axis=1, inplace=True); pro.drop_duplicates(subset=None, keep='first', inplace=True) @@ -414,12 +421,14 @@ def _fixes_genomic_symbols(self) -> pandas.DataFrame: else: clean_dates.append(x) merged_data['symbol'] = clean_dates; merged_data.fillna('None', inplace=True) # make sure that all gene and transcript type columns have none recoded to unknown or not protein-coding - merged_data['hgnc_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False) - merged_data['ensembl_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False) - merged_data['entrez_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False) - merged_data['master_gene_type'].str.replace('None', 'unknown', inplace=True, regex=False) - merged_data['master_transcript_type'].str.replace('None', 'not protein-coding', inplace=True, regex=False) - merged_data['ensembl_transcript_type'].str.replace('None', 'unknown', inplace=True, regex=False) + merged_data['hgnc_gene_type'] = merged_data['hgnc_gene_type'].str.replace('None', 'unknown', regex=False) + merged_data['ensembl_gene_type'] = merged_data['ensembl_gene_type'].str.replace('None', 'unknown', regex=False) + merged_data['entrez_gene_type'] = merged_data['entrez_gene_type'].str.replace('None', 'unknown', regex=False) + merged_data['master_gene_type'] = merged_data['master_gene_type'].str.replace('None', 'unknown', regex=False) + merged_data['master_transcript_type'] = merged_data['master_transcript_type'].str.replace( + 'None', 'not protein-coding', regex=False) + merged_data['ensembl_transcript_type'] = merged_data['ensembl_transcript_type'].str.replace( + 'None', 'unknown', regex=False) merged_data_clean = merged_data.drop_duplicates() return merged_data_clean @@ -498,6 +507,50 @@ def creates_master_genomic_identifier_map(self) -> Dict: return reformatted_mapped_identifiers + def _write_genomic_entity_metadata(self): + """Process the dictionary created in the prior steps in order to assist with creating a master metadata file + for all nodes that are a genomic entity (i.e., genes, transcripts, or proteins). + """ + + reformatted_mapped_identifiers = self.creates_master_genomic_identifier_map() + out_location = self.temp_dir + '/GENOMIC_ENTITY_METADATA.jsonl' + + for key, value in tqdm(reformatted_mapped_identifiers.items()): + old_prefix = '_'.join(key.split('_')[0:-1]); idx = key.split('_')[-1]; pass_var = True; new_prefix = None + if old_prefix == 'entrez_id': new_prefix = 'NCBIGene' + elif old_prefix in ['ensembl_gene_id', 'protein_stable_id', 'transcript_stable_id']: new_prefix = 'ensembl' + elif old_prefix == 'pro_id': new_prefix = 'PR' + else: pass_var = False + if pass_var and new_prefix is not None: + updated_key = new_prefix + ':' + idx; master_metadata_dict = {updated_key: {}} + for x in value: + i, j = '_'.join(x.split('_')[0:-1]), x.split('_')[-1] + if 'type' in i: continue + elif i == 'entrez_id': new_i = 'NCBIGene'; j = new_i + ':' + j + elif i == 'ensembl_gene_id': new_i = 'ensembl gene'; j = 'ensembl:' + j + elif i == 'protein_stable_id': new_i = 'ensembl protein'; j = 'ensembl:' + j + elif i == 'transcript_stable_id': new_i = 'ensembl transcript'; j = 'ensembl:' + j + elif i == 'pro_id_PR': new_i = 'PR'; j = new_i + ':' + j + elif i == 'hgnc_id': new_i = 'HGNC_ID'; j = new_i + ':' + j + elif i == 'uniprot_id': new_i = 'uniprot'; j = new_i + ':' + j + elif i == 'symbol': new_i = 'GeneSymbol'; j = new_i + ':' + j + else: + if i == 'synonyms': new_i = 'Synonyms' + elif i == 'name': new_i = 'Label' + elif i == 'Other_designations': new_i = 'Synonyms'; j = j.split('|') + else: new_i = i + if new_i in master_metadata_dict[updated_key].keys(): + if isinstance(j, list): master_metadata_dict[updated_key][new_i] += j + else: master_metadata_dict[updated_key][new_i] += [j] + else: master_metadata_dict[updated_key][new_i] = [j] + # write entry + dump_jsonl([master_metadata_dict], out_location) + + # load data to cloud + uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, '/GENOMIC_ENTITY_METADATA.jsonl') + + return None + def generates_specific_genomic_identifier_maps(self) -> None: """Method takes a list of information needed to create mappings between specific sets of genomic identifiers. @@ -511,23 +564,35 @@ def generates_specific_genomic_identifier_maps(self) -> None: reformatted_mapped_identifiers = self.creates_master_genomic_identifier_map() gene_sets = [ ['ENSEMBL_GENE_ENTREZ_GENE_MAP.txt', 'ensembl_gene_id', 'entrez_id', 'ensembl_gene_type', - 'entrez_gene_type', 'gene_type_update', 'gene_type_update', False, False], + 'entrez_gene_type', 'gene_type_update', 'gene_type_update', [1, 1, 'NCBIGene_']], ['ENSEMBL_TRANSCRIPT_PROTEIN_ONTOLOGY_MAP.txt', 'transcript_stable_id', 'pro_id', 'ensembl_transcript_type', - None, 'transcript_type_update', None, False, True], + None, 'transcript_type_update', None, [0, 5, 'ensembl_']], ['ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt', 'entrez_id', 'transcript_stable_id', 'entrez_gene_type', - 'ensembl_transcript_type', 'gene_type_update', 'transcript_type_update', False, False], + 'ensembl_transcript_type', 'gene_type_update', 'transcript_type_update', [0, 7, 'NCBIGene_'], + [1, 1, 'ensembl_']], ['ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt', 'entrez_id', 'pro_id', 'entrez_gene_type', None, 'gene_type_update', - None, False, True], + None, [0, 5, 'NCBIGene_']], ['GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt', 'symbol', 'transcript_stable_id', 'master_gene_type', - 'ensembl_transcript_type', 'gene_type_update', 'transcript_type_update', False, False], - ['STRING_PRO_ONTOLOGY_MAP.txt', 'protein_stable_id', 'pro_id', None, None, None, None, False, True], - ['UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt', 'uniprot_id', 'pro_id', None, None, None, None, False, True] + 'ensembl_transcript_type', 'gene_type_update', 'transcript_type_update', [1, 1, 'ensembl_']], + ['STRING_PRO_ONTOLOGY_MAP.txt', 'protein_stable_id', 'pro_id', None, None, None, None, [0, 0, '9606.']], + ['UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt', 'uniprot_id', 'pro_id', None, None, None, None, None], + ['UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt', 'uniprot_id', 'entrez_id', None, 'master_gene_type', None, + 'gene_type_update', [1, 1, 'NCBIGene_']] ] for x in gene_sets: genomic_id_mapper(reformatted_mapped_identifiers, self.temp_dir + '/' + x[0], # type: ignore x[1], x[2], x[3], x[4], x[5], x[6]) # type: ignore + + if x[-1] is not None: + df = pandas.read_csv(self.temp_dir + '/' + x[0], header=None, delimiter='\t', low_memory=False) + for i in x[7:]: + df[i[1]] = i[2] + df[i[0]].astype(str) + df = df.replace('None', numpy.nan).dropna(axis=1, how="all") + df.to_csv(self.temp_dir + '/' + x[0], header=None, sep='\t', index=False) + uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, x[0]) # type: ignore + self._write_genomic_entity_metadata() # write genomic metadata return None @@ -621,12 +686,12 @@ def creates_chebi_to_mesh_identifier_mappings(self) -> None: # write results and push data to gcs bucket filename = 'MESH_CHEBI_MAP.txt' with open(self.temp_dir + '/' + filename, 'w') as out: - for pair in mesh_edges: out.write(pair[0] + '\t' + pair[1] + '\n') + for pair in mesh_edges: out.write(pair[0].replace('_', ':') + '\t' + pair[1] + '\n') uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, filename) return None - def _preprocess_mondo_mapping_data(self) -> Dict: + def _preprocess_mondo_mapping_data(self) -> pandas.DataFrame: """Method processes MonDO Disease Ontology (MONDO) ontology data in order to create a dictionary that aligns MONDO concepts with other types of disease terminology identifiers. This is done by obtaining database cross-references (dbxrefs) for each ontology and then combining the results into a single large dictionary @@ -644,9 +709,23 @@ def _preprocess_mondo_mapping_data(self) -> Dict: mondo_dict = {str(k).lower().split('/')[-1]: {str(i).split('/')[-1].replace('_', ':') for i in v} for k, v in dbxref_res.items() if 'MONDO' in str(v)} - return mondo_dict - - def _preprocess_hpo_mapping_data(self) -> Dict: + # convert to pandas DataFrame + temp_list = [] + for k, v in mondo_dict.items(): + if k.startswith('umls:'): new_k = k.split(':')[-1].upper() + elif k.startswith('hp:'): new_k = k.upper() + elif k.startswith('mesh:'): new_k = 'MESH:' + k.split(':')[-1].upper() + elif k.startswith('orphanet:'): new_k = 'ORPHA:' + k.split(':')[-1].upper() + elif k.startswith('omimps:'): new_k = 'OMIM:' + k.split(':')[-1].upper() + else: new_k = k + for i in v: + temp_list += [[new_k, i.replace(':', '_')]]; temp_list += [[i, i.replace(':', '_')]] + # convert to + mondo_df = pandas.DataFrame({'other_id': [x[0] for x in temp_list], 'ontology_id': [x[1] for x in temp_list]}) + + return mondo_df + + def _preprocess_hpo_mapping_data(self) -> pandas.DataFrame: """Method processes Human Phenotype Ontology (HPO) ontology data in order to create a dictionary that aligns HPO concepts with other types of disease terminology identifiers. This is done by obtaining database cross-references (dbxrefs) for each ontology and then combining the results into a single large dictionary @@ -664,12 +743,89 @@ def _preprocess_hpo_mapping_data(self) -> Dict: hp_dict = {str(k).lower().split('/')[-1]: {str(i).split('/')[-1].replace('_', ':') for i in v} for k, v in dbxref_res.items() if 'HP' in str(v)} - return hp_dict + # convert to pandas DataFrame + temp_list = [] + for k, v in hp_dict.items(): + if k.startswith('umls:'): new_k = k.split(':')[-1].upper() + elif k.startswith('mondo:'): new_k = k.upper() + elif k.startswith('msh:'): new_k = 'MESH:' + k.split(':')[-1].upper() + elif k.startswith('orpha:'): new_k = 'ORPHA:' + k.split(':')[-1].upper() + else: new_k = k + for i in v: + temp_list += [[new_k, i.replace(':', '_')]]; temp_list += [[i, i.replace(':', '_')]] + # convert to + hp_df = pandas.DataFrame({'other_id': [x[0] for x in temp_list], 'ontology_id': [x[1] for x in temp_list]}) + + return hp_df + + def reads_disgenet_data(self) -> pandas.DataFrame: + """Reads in disease mapping data from DisGeNET. + + Returns: + data: A pandas DataFrame object. + """ + + data = self.reads_gcs_bucket_data_to_df(f_name='disease_mappings.tsv', delm='\t', head=0) + + # reformat data + data['vocabulary'] = data['vocabulary'].str.lower() + data['diseaseId'] = data['diseaseId'].str.lower() + data['vocabulary'] = data['vocabulary'].str.replace('hpo', 'HP') + data['vocabulary'] = data['vocabulary'].str.replace('mondo', 'MONDO') + data['vocabulary'] = data['vocabulary'].str.replace('msh', 'MESH') + data['vocabulary'] = data['vocabulary'].str.replace('omim', 'OMIM') + data['vocabulary'] = data['vocabulary'].str.replace('do', 'doid') + data['vocabulary'] = data['vocabulary'].str.replace('ordo', 'ORPHA') + data['vocabulary'] = data['vocabulary'].str.replace('ORPHAid', 'ORPHA') + # capitalize UMLS id + data['diseaseId'] = data['diseaseId'].str.upper() + # create a disease code column + data['code'] = data['vocabulary'] + ':' + data['code'] + data['code'] = data['code'].str.replace('HP:HP:', 'HP:') + # rename columns + data.rename(columns={'diseaseId': 'cui', 'vocabularyName': 'code_name'}, inplace=True) + # remove unneeded columns + data = data[['cui', 'code', 'code_name', 'vocabulary']].drop_duplicates() + + return data + + def reads_medgen_data(self) -> pandas.DataFrame: + """Reads in disease mapping data from MedGen. + + Returns: + data: A pandas DataFrame object. + """ + + data = self.reads_gcs_bucket_data_to_df(f_name='MGCONSO.RRF', delm='|', head=0) + + # reformat data + data = data[data['SUPPRESS'] == 'N'].drop_duplicates() + data = data[data['SAB'].isin(['HPO', 'MONDO', 'MSH', 'ORDO', 'OMIM'])].drop_duplicates() + # reformat codes + data['temp_code'] = data.apply(lambda x: 'MESH:' + x['CODE'] if x['SAB'] == 'MSH' + else 'OMIM:' + x['CODE'] if x['SAB'] == 'OMIM' + else 'ORPHA:' + x['SDUI'].split('_')[-1] if x['SAB'] == 'ORDO' + else x['SDUI'] if x['SAB'] == 'HPO' + else x['SDUI'] if x['SAB'] == 'MONDO' + else 'None', axis=1) + # add rows for MedGen identifiers + temp = data[['#CUI']]; temp['temp_code'] = 'MedGen:' + data['#CUI'] + data = pandas.concat([data, temp]) + # remove unneeded columns + data = data[['#CUI', 'temp_code', 'STR', 'SAB']].drop_duplicates() + # rename columns + data.rename(columns={'#CUI': 'cui', 'STR': 'code_name', + 'temp_code': 'code', 'SAB': 'vocabulary'}, inplace=True) + # reformat vocabulary ids + data['vocabulary'] = data['vocabulary'].str.replace('HPO', 'HP') + data['vocabulary'] = data['vocabulary'].str.replace('MSH', 'MESH') + + return data def creates_disease_identifier_mappings(self) -> None: """Creates Human Phenotype Ontology (HPO) and MonDO Disease Ontology (MONDO) dbxRef maps and then uses them - with the DisGEeNET UMLS disease mappings to create a master mapping between all disease identifiers to HPO - and MONDO. + with the DisGEeNET UMLS and MedGen disease mappings to create a master mapping between all disease identifiers + to HPO and MONDO. Returns: None. @@ -677,44 +833,42 @@ def creates_disease_identifier_mappings(self) -> None: log_str = 'Creating Phenotype and Disease ID Cross-Map Data'; print(log_str); logger.info(log_str) - mondo_dict, hp_dict = self._preprocess_mondo_mapping_data(), self._preprocess_hpo_mapping_data() - data = self.reads_gcs_bucket_data_to_df(f_name='disease_mappings.tsv', delm='\t', head=0) - data['vocabulary'], data['diseaseId'] = data['vocabulary'].str.lower(), data['diseaseId'].str.lower() - data['vocabulary'] = ['doid' if x == 'do' else 'ordoid' if x == 'ordo' else x for x in data['vocabulary']] - # get all CUIs mapped to HPO and MONDO - ont_dict: Dict = {}; disease_data_keep = data.query('vocabulary == "hpo" | vocabulary == "mondo"') - for idx, row in tqdm(disease_data_keep.iterrows(), total=disease_data_keep.shape[0]): - if row['vocabulary'] == 'mondo': key, value = 'umls:' + row['diseaseId'], 'MONDO:' + row['code'] - else: key, value = 'umls:' + row['diseaseId'], row['code'] - if key in ont_dict.keys(): ont_dict[key] |= {value} - else: ont_dict[key] = {value} - for key in tqdm(ont_dict.keys()): # add ontology mappings from MONDO and HPO - if key in mondo_dict.keys(): ont_dict[key] = set(list(ont_dict[key]) + list(mondo_dict[key])) - if key in hp_dict.keys(): ont_dict[key] = set(list(ont_dict[key]) + list(hp_dict[key])) - # get all rows for HPO/MONDO CUIs to obtain mappings to other disease identifiers - disease_dict: Dict = {}; disease_data_other = data[data.diseaseId.isin(disease_data_keep['diseaseId'])] - for idx, row in tqdm(disease_data_other.iterrows(), total=disease_data_other.shape[0]): - vocab, ids, code = row['vocabulary'], row['diseaseId'], row['code'] - if vocab == 'mondo' or vocab == 'hpo': - key, value = 'umls:' + ids.lower(), code - if key in disease_dict.keys(): disease_dict[key] |= {value} - else: disease_dict[key] = {value} - else: - if 'mondo' not in code or 'hp' not in code: - if ':' not in code: key, value = vocab + ':' + code, ont_dict['umls:' + ids] - else: key, value = code, ont_dict['umls:' + ids] - if key in disease_dict.keys(): disease_dict[key] |= value - else: disease_dict[key] = value - # save data and push to GCS bucket - file1, file2 = 'DISEASE_MONDO_MAP.txt', 'PHENOTYPE_HPO_MAP.txt' - with open(self.temp_dir + '/' + file1, 'w') as out1, open(self.temp_dir + '/' + file2, 'w') as out2: - for k, v in tqdm({**disease_dict, **mondo_dict, **hp_dict}.items()): - if any(x for x in v if x.startswith('MONDO')): - for idx in [x.replace(':', '_') for x in v if 'MONDO' in x]: - out1.write(k.upper().split(':')[-1] + '\t' + idx + '\n') - if any(x for x in v if x.startswith('HP')): - for idx in [x.replace(':', '_') for x in v if 'HP' in x]: - out2.write(k.upper().split(':')[-1] + '\t' + idx + '\n') + disease_map_df = pandas.concat([self._preprocess_mondo_mapping_data(), self._preprocess_hpo_mapping_data()]) + disease_data = pandas.concat([self.reads_disgenet_data(), self.reads_medgen_data()]).drop_duplicates() + + # find cuis that map to HP or MONDO + disease_data_keep = disease_data.copy() + disease_data_keep = disease_data_keep.query('vocabulary == "HP" | vocabulary == "MONDO"') + disease_data_keep = disease_data_keep[['cui', 'code']] + cui_list = set(disease_data_keep['cui']) + # obtain a list of other ids that map to the cuis + temp_df = disease_data[disease_data['cui'].isin(cui_list)] + # merge back with original data and rename the columns + merged_temp = temp_df.merge(disease_data_keep, on='cui') + merged_temp = merged_temp[['code_x', 'code_y', 'code_name', 'vocabulary']].drop_duplicates() + merged_temp.rename(columns={'code_x': 'cui', 'code_y': 'code'}, inplace=True) + # combine the columns back to main data + disease_mapping_data = pandas.concat([disease_data, merged_temp]).drop_duplicates() + disease_mapping_data = disease_mapping_data[['cui', 'code']].drop_duplicates() + # merge ontology and other mappings together and clean up file + cleaned_disease_map = disease_mapping_data.merge(disease_map_df, left_on='cui', right_on='other_id') + cleaned_disease_map = cleaned_disease_map[['cui', 'ontology_id']] + cleaned_disease_map.rename(columns={'cui': 'disease_id'}, inplace=True) + # format ontology identifiers + cleaned_disease_map['ontology_id'] = cleaned_disease_map['ontology_id'].str.replace(':', '_') + cleaned_disease_map['vocabulary'] = cleaned_disease_map['ontology_id'].str.replace('\_.*', '', regex=True) + cleaned_disease_map.drop_duplicates(inplace=True) + + # write data + # split data by ontology and write to file + mondo_map = cleaned_disease_map[cleaned_disease_map['vocabulary'] == 'MONDO'].drop_duplicates() + hp_map = cleaned_disease_map[cleaned_disease_map['vocabulary'] == 'HP'].drop_duplicates() + mondo_map = mondo_map[['disease_id', 'ontology_id']]; hp_map = hp_map[['disease_id', 'ontology_id']] + + # write data + file1 = 'DISEASE_MONDO_MAP.txt'; file2 = 'PHENOTYPE_HPO_MAP.txt' + mondo_map.to_csv(self.temp_dir + '/' + file1, header=None, index=False, sep='\t') + hp_map.to_csv(self.temp_dir + '/' + file2, header=None, index=False, sep='\t') uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, file1) uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, file2) @@ -766,25 +920,16 @@ def _extracts_hpa_tissue_information(self) -> pandas.DataFrame: return hpa - def processes_hpa_gtex_data(self) -> None: - """Method processes and combines gene expression experiment results from the Human protein Atlas (HPA) and the - Genotype-Tissue Expression Project (GTEx). Additional details provided below on how each source are processed. - - HPA: The HPA data is reformatted so all tissue, cell, cell lines, and fluid types are stored as a nested - list. The anatomy type is specified as an item in the list according to its type. - - GTEx: All protein-coding genes that appear in the HPA data set are removed. Then, only those non-coding - genes with a median expression >= 1.0 are maintained. GTEx data are formatted such the anatomical - entities are stored as columns and genes stored as rows, thus the expression filtering step is - performed while also reformatting the file, resulting in a nested list. + def _processes_hpa_data(self) -> Union[List, pandas.DataFrame]: + """The HPA data is reformatted so all tissue, cell, cell lines, and fluid types are stored as a nested list. + The anatomy type is specified as an item in the list according to its type. Returns: - None. + hpa_results: A nested list of processed HPA data. """ - log_str = 'Creating Human Protein Atlas and GTEx Cross-Map Data'; print(log_str); logger.info(log_str) + hpa = self._extracts_hpa_tissue_information() - hpa = self._extracts_hpa_tissue_information(); f_name = 'GTEx_Analysis_*_RNASeQC*_gene_median_tpm.gct' - gtex = self.reads_gcs_bucket_data_to_df(f_name=f_name, delm='\t', skip=2, head=0) - gtex.fillna('None', inplace=True); gtex['Name'].str.replace('(\..*)', '', inplace=True, regex=True) # process human protein atlas data hpa_results = [] for idx, row in tqdm(hpa.iterrows(), total=hpa.shape[0]): @@ -795,10 +940,10 @@ def processes_hpa_gtex_data(self) -> None: if ';' in row_val: for x in row_val.split(';'): x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1]) - hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] + hpa_results += [[ens, gene, uni, evid, 'anatomy', 'None', x1, x2, source]] else: x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1]) - hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] + hpa_results += [[ens, gene, uni, evid, 'anatomy', 'None', x1, x2, source]] if row['RNA cell line specific nTPM'] != 'None': row_val = row['RNA cell line specific nTPM'] if ';' in row_val: @@ -813,10 +958,10 @@ def processes_hpa_gtex_data(self) -> None: if ';' in row_val: for x in row_val.split(';'): x1 = str(x.split(':')[0]); x2 = float(x.split(': ')[1]) - hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] + hpa_results += [[ens, gene, uni, evid, 'anatomy', 'None', x1, x2, source]] else: x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1]) - hpa_results += [[ens, gene, uni, evid, 'anatomy', sub, x1, x2, source]] + hpa_results += [[ens, gene, uni, evid, 'anatomy', 'None', x1, x2, source]] if row['RNA blood cell specific nTPM'] != 'None': row_val = row['RNA blood cell specific nTPM'] if ';' in row_val: @@ -835,8 +980,28 @@ def processes_hpa_gtex_data(self) -> None: else: x1 = str(row_val.split(':')[0]); x2 = float(row_val.split(': ')[1]) hpa_results += [[ens, gene, uni, evid, 'cell line', sub, x1, x2, source]] + + return hpa_results, hpa + + def _processes_gtex_data(self, hpa_df: pandas.DataFrame) -> List: + """All protein-coding genes that appear in the HPA data set are removed. Then, only those non-coding genes + with a median expression >= 1.0 are maintained. GTEx data are formatted such the anatomical entities are + stored as columns and genes stored as rows, thus the expression filtering step is performed while also + reformatting the file, resulting in a nested list. + + Args: + hpa_df: A Pandas DataFrame containining HPA data. + + Returns: + gtex_results: A nested list of processed HPA data. + """ + + f_name = 'GTEx_Analysis_*_RNASeQC*_gene_median_tpm.gct' + gtex = self.reads_gcs_bucket_data_to_df(f_name=f_name, delm='\t', skip=2, head=0) + gtex.fillna('None', inplace=True); gtex['Name'] = gtex['Name'].str.replace('(\..*)', '', regex=True) + # process gtex data -- using only those protein-coding genes not already in hpa - gtex_results, hpa_genes = [], list(hpa['Ensembl'].drop_duplicates(keep='first', inplace=False)) + gtex_results, hpa_genes = [], list(hpa_df['Ensembl'].drop_duplicates(keep='first', inplace=False)) gtex = gtex.loc[gtex['Name'].apply(lambda i: i not in hpa_genes)] # loop over data and re-organize source = 'Genotype-Tissue Expression (GTEx) Project' @@ -845,6 +1010,22 @@ def processes_hpa_gtex_data(self) -> None: typ = 'cell line' if 'Cells' in col else 'anatomy'; evid = 'Evidence at transcript level' gtex_results += [[str(row['Name']), str(row['Description']), 'None', evid, typ, 'None', col, float(row[col]), source]] + + return gtex_results + + def processes_hpa_gtex_data(self) -> None: + """Method processes and combines gene expression experiment results from the Human protein Atlas (HPA) and the + Genotype-Tissue Expression Project (GTEx). Additional details provided below on how each source are processed. + + Returns: + None. + """ + + log_str = 'Creating Human Protein Atlas and GTEx Cross-Map Data'; print(log_str); logger.info(log_str) + + hpa_results, hpa = self._processes_hpa_data() + gtex_results = self._processes_gtex_data(hpa) + # write results filename = 'HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt' with open(self.temp_dir + '/' + filename, 'w') as out: @@ -1294,6 +1475,119 @@ def processes_relation_ontology_data(self) -> None: return None + def _processes_variant_summary_data(self) -> pandas.DataFrame: + """Data from ClinVar (variant_summary) is downloaded and the file is cleaned to handle missing data, unneeded + variables are removed, identifiers and date fields are cleaned and reformatted, and rows without valid + disease/phenotype identifiers are removed. + + Returns: + var_summary_update: A Pandas DataFrame containing processed clinvar data. + """ + + var_summary = self.reads_gcs_bucket_data_to_df(f_name='variant_summary.txt', delm='\t', head=0) + + # replace "na" and "-" with NaN + var_summary = var_summary.replace('na', numpy.nan); var_summary = var_summary.replace('-', numpy.nan) + # handle ids that are coded as missing (i.e., -1) + var_summary['GeneID'] = var_summary['GeneID'].replace(-1, numpy.nan) + var_summary['RS# (dbSNP)'] = var_summary['RS# (dbSNP)'].replace(-1, numpy.nan) + # convert date format + var_summary['LastEvaluated'] = var_summary['LastEvaluated'].str.replace('None', '') + var_summary['LastEvaluated'] = pandas.to_datetime(var_summary['LastEvaluated']) + var_summary['LastEvaluated'] = var_summary['LastEvaluated'].dt.strftime('%B %d, %Y') + var_summary['LastEvaluated'] = var_summary['LastEvaluated'].replace('', numpy.nan) + # rename variables + var_summary.rename(columns={'#AlleleID': 'AlleleID', 'nsv/esv (dbVar)': 'nsv', 'Name': 'VariantName'}, + inplace=True) + # update variable types + var_summary['GeneID'] = var_summary['GeneID'].astype('Int64') + var_summary['RS# (dbSNP)'] = var_summary['RS# (dbSNP)'].astype('Int64') + + # subset df to process multiple assembly entries + var_summary_update_assemb = var_summary.copy() + var_summary_update_assemb = var_summary_update_assemb[['VariationID', 'Assembly', 'ChromosomeAccession', + 'Chromosome', 'Start', 'Stop', 'ReferenceAllele', + 'AlternateAllele', 'Cytogenetic', + 'PositionVCF']].drop_duplicates() + # identify columns to process + assemb_cols = ['ChromosomeAccession', 'Chromosome', 'Start', 'Stop', 'ReferenceAllele', + 'AlternateAllele', 'Cytogenetic', 'PositionVCF', 'ReferenceAlleleVCF', 'AlternateAlleleVCF'] + # group data by variant + df = var_summary_update_assemb.fillna('None') + df = df.groupby('VariationID').apply( + lambda g: str(g.drop(['VariationID'], axis=1).to_dict('records'))).to_dict() + # convert to Pandas DataFrame + df_items = df.items() + temp_df = pandas.DataFrame({'VariationID': [x[0] for x in df_items], 'Assembly': [x[1] for x in df_items]}) + # join temp df with original data + var_summary_assemb = var_summary.copy().drop(assemb_cols + ['Assembly'], axis=1) + var_summary_update = var_summary_assemb.merge(temp_df, on='VariationID', how='left') + var_summary_update.drop_duplicates(inplace=True) # drop duplicates + + # process and clean up phenotype identifiers + var_summary_update['Phenotype'] = var_summary_update['PhenotypeIDS'].str.replace('|', ';').str.replace(',', ';') + var_summary_update['OtherIDs'] = var_summary_update['OtherIDs'].str.replace(';', '|').str.replace(',', '|') + # remove unneeded variables + drop_list = ['PhenotypeList', 'PhenotypeIDS'] + var_summary_update = var_summary_update.drop(drop_list, axis=1).drop_duplicates() + # replace NaN with 'None' + var_summary_update['Phenotype'] = var_summary_update['Phenotype'].fillna('None') + # reformat phenotypeIDS and trim leading whitespace from unnested columns + var_summary_update['Phenotype'] = var_summary_update['Phenotype'].apply( + lambda x: ';'.join(set(x for x in ['MONDO:' + i.split(':')[-1] if i.startswith('MONDO') + else 'HP:' + i.split(':')[-1] if i.startswith('Human Phenotype') + else 'ORPHA:' + i.split(':')[-1] if i.startswith('Orphanet') + else 'None' if i.endswith(' conditions') + else i for i in x.split(';')] if x != 'None'))) + var_summary_update.drop_duplicates(inplace=True) # drop duplicates + + return var_summary_update + + def _processes_var_citation_data(self) -> pandas.DataFrame: + """Data from ClinVar (var_citations) is downloaded and the file is cleaned to handle missing data, unneeded + variables are removed, and a new citation field is created. + + Returns: + var_citations: A Pandas DataFrame containing processed clinvar data. + """ + + var_citations = self.reads_gcs_bucket_data_to_df(f_name='var_citations.txt', delm='\t', head=0) + + # replace "na" and "-" with NaN + var_citations = var_citations.replace('na', numpy.nan); var_citations = var_citations.replace('-', numpy.nan) + # combine citation information + var_citations['Citation'] = var_citations['citation_source'] + ':' + var_citations['citation_id'] + # remove unneeded variables + drop_list = ['citation_source', 'citation_id'] + var_citations = var_citations.drop(drop_list, axis=1).drop_duplicates() + # group data by citations + var_citations = var_citations.groupby('VariationID').Citation.agg([('Citation', '|'.join)]).reset_index() + var_citations = var_citations.drop_duplicates().sort_values(by=['VariationID']) + + return var_citations + + def _processes_allele_gene_data(self) -> pandas.DataFrame: + """Data from ClinVar (allele_gene) is downloaded and the file is cleaned to handle missing data, unneeded + variables are removed, and the file is reduced to only contain a subset of relevant variables. + + Returns: + allele_gene: A Pandas DataFrame containing processed clinvar data. + """ + + allele_gene = self.reads_gcs_bucket_data_to_df(f_name='allele_gene.txt', delm='\t', head=0) + + # replace "na" and "-" with NaN + allele_gene = allele_gene.replace('na', numpy.nan) + allele_gene = allele_gene.replace('-', numpy.nan) + # handle gene ids that may be coded as -1 + allele_gene['GeneID'] = allele_gene['GeneID'].replace(-1, numpy.nan) + # rename variables + allele_gene.rename(columns={'#AlleleID': 'AlleleID', 'Symbol': 'GeneSymbol', 'Name': 'GeneName'}, inplace=True) + # update variable types + allele_gene['GeneID'] = allele_gene['GeneID'].astype('Int64') + + return allele_gene + def processes_clinvar_data(self) -> None: """Processes ClinVar data by performing light tidying and filtering and then outputs data needed to create mappings between genes, variants, and phenotypes. @@ -1304,16 +1598,48 @@ def processes_clinvar_data(self) -> None: log_str = 'Generating ClinVar Cross-Mapping Data'; print(log_str); logger.info(log_str) - f_name = 'variant_summary.txt' - clinvar_data = self.reads_gcs_bucket_data_to_df(f_name=f_name, delm='\t', head=0) - clinvar_data.fillna('None', inplace=True) - # explode nested data - explode_df_clinvar = explodes_data(clinvar_data.copy(), ['PhenotypeIDS'], ';') - explode_df_clinvar = explodes_data(explode_df_clinvar.copy(), ['PhenotypeIDS'], ',') - explode_df_clinvar['PhenotypeIDS'].str.replace('Orphanet:ORPHA', 'ORPHA:', inplace=True, regex=True) - explode_df_clinvar['PhenotypeIDS'].str.replace('Human Phenotype Ontology:HP:', 'HP_', inplace=True, regex=True) - filename = 'CLINVAR_VARIANT_GENE_DISEASE_PHENOTYPE_EDGES.txt' - explode_df_clinvar.to_csv(self.temp_dir + '/' + filename, sep='\t', encoding='utf-8', index=False) + # obtain processed data sets + var_summary_update = self._processes_variant_summary_data() + var_citations = self._processes_var_citation_data() + allele_gene = self._processes_allele_gene_data() + + # merge var_summary and var_citation data + merge_cols = list(set(var_summary_update.columns).intersection(set(var_citations.columns))) + var_summary_merged = var_summary_update.merge(var_citations, on=merge_cols, how='left') + # added allele_gene data + merge_cols = list(set(var_summary_merged.columns).intersection(set(allele_gene.columns))) + var_summary_merged = var_summary_merged.merge(allele_gene, on=merge_cols, how='left') + var_summary_merged['GenesPerAlleleID'] = var_summary_merged['GenesPerAlleleID'].astype('Int64') + # reduce data set to extract variant gene edges + var_summary_merged_gene = var_summary_merged.copy() + var_summary_merged_gene = var_summary_merged_gene[[ + 'VariationID', 'AlleleID', 'RS# (dbSNP)', 'Type', 'VariantName', 'OtherIDs', 'GeneID', 'GeneSymbol', + 'GeneName', 'GenesPerAlleleID', 'Assembly', 'Category', 'Guidelines', 'TestedInGTR', 'RCVaccession', + 'LastEvaluated', 'ReviewStatus', 'ClinicalSignificance', 'ClinSigSimple', 'Origin', 'OriginSimple', + 'Source', 'SubmitterCategories', 'NumberSubmitters', 'Citation']] + var_summary_merged_gene.drop_duplicates(inplace=True) + var_summary_merged_gene = var_summary_merged_gene.dropna(subset=['GeneID']) + var_summary_merged_gene['GeneID'] = 'NCBIGene_' + var_summary_merged_gene['GeneID'].astype(str) + var_summary_merged_gene['VariationID'] = 'clinvar_' + var_summary_merged_gene['VariationID'].astype(str) + filename = 'CLINVAR_VARIANT_GENE_EDGES.txt' + var_summary_merged_gene.to_csv(self.temp_dir + '/' + filename, sep='\t', encoding='utf-8', index=False) + uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, filename) + # reduce data set to extract variant-disease/phenotype edges + var_summary_merged_disease = var_summary_merged.copy() + var_summary_merged_disease = var_summary_merged_disease[[ + 'VariationID', 'RS# (dbSNP)', 'Type', 'VariantName', 'RCVaccession', 'LastEvaluated', 'ReviewStatus', + 'ClinicalSignificance', 'ClinSigSimple', 'NumberSubmitters', 'SubmitterCategories', 'Guidelines', + 'GeneID', 'TestedInGTR', 'Origin', 'OriginSimple', 'Assembly', 'Phenotype', 'Citation', 'OtherIDs']] + var_summary_merged_disease.drop_duplicates(inplace=True) + # expand results by disease identifier, remove phenotype rows with None, and drop duplicates + cols = ['Phenotype'] + for col in tqdm(cols): var_summary_merged_disease = var_summary_merged_disease.assign( + **{col: var_summary_merged_disease[col].str.split(';')}).explode(col) + var_summary_merged_disease = var_summary_merged_disease[var_summary_merged_disease['Phenotype'] != 'None'] + var_summary_merged_disease.drop_duplicates(inplace=True) + var_summary_merged_disease['VariationID'] = 'clinvar_' + var_summary_merged_disease['VariationID'].astype(str) + filename = 'CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt' + var_summary_merged_gene.to_csv(self.temp_dir + '/' + filename, sep='\t', encoding='utf-8', index=False) uploads_data_to_gcs_bucket(self.bucket, self.processed_data, self.temp_dir, filename) return None @@ -1353,143 +1679,143 @@ def processes_cofactor_catalyst_data(self) -> None: return None - def _creates_gene_metadata_dict(self) -> Dict: - """Creates a dictionary to store labels, synonyms, and a description for each Entrez gene identifier present in - the input data file. - - Returns: - gene_metadata_dict: A dict containing metadata that's keyed by Entrez gene identifier and whose values are - dicts containing label, description, and synonym information. For example: - {{'http://www.ncbi.nlm.nih.gov/gene/1': { - 'Label': 'A1BG', - 'Description': "A1BG is 'protein-coding' and is located on chromosome 19 (19q13.43).", - 'Synonym': 'HEL-S-163pA|A1B|ABG|HYST2477alpha-1B-glycoprotein|GAB'}, ...} - """ - - log_str = 'Generating Metadata for Gene Identifiers'; print('\t- ' + log_str); logger.info(log_str) - - f_name = 'Homo_sapiens.gene_info' - x = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name, self.temp_dir) - data = pandas.read_csv(x, header=0, delimiter='\t', low_memory=False) - data = data.loc[data['#tax_id'].apply(lambda i: i == 9606)] - data.fillna('None', inplace=True); data.replace('-', 'None', inplace=True, regex=False) - # create metadata - genes, lab, desc, syn = [], [], [], [] - for idx, row in tqdm(data.iterrows(), total=data.shape[0]): - gene_id, sym, defn, gene_type = row['GeneID'], row['Symbol'], row['description'], row['type_of_gene'] - chrom, map_loc, s1, s2 = row['chromosome'], row['map_location'], row['Synonyms'], row['Other_designations'] - if gene_id != 'None': - genes.append('http://www.ncbi.nlm.nih.gov/gene/' + str(gene_id)) - if sym != 'None' or sym != '': lab.append(sym) - else: lab.append('Entrez_ID:' + gene_id) - if 'None' not in [defn, gene_type, chrom, map_loc]: - desc_str = "{} has locus group '{}' and is located on chromosome {} ({})." - desc.append(desc_str.format(sym, gene_type, chrom, map_loc)) - else: desc.append("{} locus group '{}'.".format(sym, gene_type)) - if s1 != 'None' and s2 != 'None': - syn.append('|'.join(set([x for x in (s1 + s2).split('|') if x != 'None' or x != '']))) - elif s1 != 'None': syn.append('|'.join(set([x for x in s1.split('|') if x != 'None' or x != '']))) - elif s2 != 'None': syn.append('|'.join(set([x for x in s2.split('|') if x != 'None' or x != '']))) - else: syn.append('None') - # combine into new data frame then convert it to dictionary - metadata = pandas.DataFrame(list(zip(genes, lab, desc, syn)), columns=['ID', 'Label', 'Description', 'Synonym']) - metadata = metadata.astype(str); metadata.drop_duplicates(subset='ID', inplace=True) - metadata.set_index('ID', inplace=True); gene_metadata_dict = metadata.to_dict('index') - - return gene_metadata_dict - - def _creates_transcript_metadata_dict(self) -> Dict: - """Creates a dictionary to store labels, synonyms, and a description for each Entrez gene identifier present - in the input data file. - - Returns: - rna_metadata_dict: A dict containing metadata that's keyed by Ensembl transcript identifier and whose values - are a dict containing label, description, and synonym information. For example: - {'https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=ENST00000456328': { - 'Label': 'DDX11L1-202', - 'Description': "Transcript DDX11L1-202 is classified as type 'processed_transcript'.", - 'Synonym': 'None'}, ...} - """ - - log_str = 'Generating Metadata for Transcript Identifiers'; print('\t- ' + log_str); logger.info(log_str) - - f_name = 'ensembl_identifier_data_cleaned.txt' - x = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name, self.temp_dir) - dup_cols = ['transcript_stable_id', 'transcript_name', 'ensembl_transcript_type'] - data = pandas.read_csv(x, header=0, delimiter='\t', low_memory=False) - data = data.loc[data['transcript_stable_id'].apply(lambda i: i != 'None')] - data.drop(['ensembl_gene_id', 'symbol', 'protein_stable_id', 'uniprot_id', 'master_transcript_type', - 'entrez_id', 'ensembl_gene_type', 'master_gene_type', 'symbol'], axis=1, inplace=True) - data.drop_duplicates(subset=dup_cols, keep='first', inplace=True); data.fillna('None', inplace=True) - # create metadata - rna, lab, desc, syn = [], [], [], [] - for idx, row in tqdm(data.iterrows(), total=data.shape[0]): - rna_id, ent_type, nme = row[dup_cols[0]], row[dup_cols[2]], row[dup_cols[1]] - rna.append('https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=' + rna_id) - if nme != 'None': lab.append(nme) - else: lab.append('Ensembl_Transcript_ID:' + rna_id); nme = 'Ensembl_Transcript_ID:' + rna_id - if ent_type != 'None': desc.append("Transcript {} is classified as type '{}'.".format(nme, ent_type)) - else: desc.append('None') - syn.append('None') - # combine into new data frame then convert it to dictionary - metadata = pandas.DataFrame(list(zip(rna, lab, desc, syn)), columns=['ID', 'Label', 'Description', 'Synonym']) - metadata = metadata.astype(str); metadata.drop_duplicates(subset='ID', inplace=True) - metadata.set_index('ID', inplace=True); rna_metadata_dict = metadata.to_dict('index') - - return rna_metadata_dict - - def _creates_variant_metadata_dict(self) -> Dict: - """Creates a dictionary to store labels, synonyms, and a description for each ClinVar variant identifier present - in the input data file. - - Returns: - variant_metadata_dict: A dict containing metadata that's keyed by ClinVar variant identifier and whose - values are a dict containing label, description, and synonym information. For example: - {{'https://www.ncbi.nlm.nih.gov/snp/rs141138948': { - 'Label': 'NM_016042.4(EXOSC3):c.395A>C (p.Asp132Ala)', - 'Description': "This variant is a germline single nucleotide variant on chromosome 9 - (NC_000009.12, start:37783993/stop:37783993 positions,cytogenetic location:9p13.2) and - has clinical significance 'Pathogenic/Likely pathogenic'. This entry is for the GRCh38 and was - last reviewed on Sep 30, 2020 with review status 'criteria provided, multiple submitters, - no conflict'.", 'Synonym': 'None'}, ...} - """ - - log_str = 'Generating Metadata for Variant IDs'; print('\t- ' + log_str); logger.info(log_str) - - f_name = 'variant_summary.txt' - x = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name, self.temp_dir) - data = pandas.read_csv(x, header=0, delimiter='\t', low_memory=False) - data = data.loc[data['Assembly'].apply(lambda i: i == 'GRCh38')] - data = data.loc[data['RS# (dbSNP)'].apply(lambda i: i != -1)] - data = data[['#AlleleID', 'Type', 'Name', 'ClinicalSignificance', 'RS# (dbSNP)', 'Origin', 'Start', 'Stop', - 'ChromosomeAccession', 'Chromosome', 'ReferenceAllele', 'Assembly', 'AlternateAllele', - 'Cytogenetic', 'ReviewStatus', 'LastEvaluated']] - data.replace('na', 'None', inplace=True); data.fillna('None', inplace=True) - data.sort_values('LastEvaluated', ascending=False, inplace=True) - data.drop_duplicates(subset='RS# (dbSNP)', keep='first', inplace=True) - # create metadata - var, label, desc, syn = [], [], [], [] - for idx, row in tqdm(data.iterrows(), total=data.shape[0]): - var_id, lab = row['RS# (dbSNP)'], row['Name'] - if var_id != 'None': - var.append('https://www.ncbi.nlm.nih.gov/snp/rs' + str(var_id)) - if lab != 'None': label.append(lab) - else: label.append('dbSNP_ID:rs' + str(var_id)) - sent = "This variant is a {} {} located on chromosome {} ({}, start:{}/stop:{} positions, " + \ - "cytogenetic location:{}) and has clinical significance '{}'. " + \ - "This entry is for the {} and was last reviewed on {} with review status '{}'." - desc.append( - sent.format(row['Origin'].str.replace(';', '/'), row['Type'].replace(';', '/'), row['Chromosome'], - row['ChromosomeAccession'], row['Start'], row['Stop'], row['Cytogenetic'], - row['ClinicalSignificance'], row['Assembly'], row['LastEvaluated'], - row['ReviewStatus']).replace('None', 'UNKNOWN')) - syn.append('None') - # combine into new data frame then convert it to dictionary - metadata = pandas.DataFrame(list(zip(var, label, desc, syn)), columns=['ID', 'Label', 'Description', 'Synonym']) - metadata.drop_duplicates(inplace=True); metadata = metadata.astype(str) - metadata.set_index('ID', inplace=True); variant_metadata_dict = metadata.to_dict('index') - - return variant_metadata_dict + # def _creates_gene_metadata_dict(self) -> Dict: + # """Creates a dictionary to store labels, synonyms, and a description for each Entrez gene identifier present in + # the input data file. + # + # Returns: + # gene_metadata_dict: A dict containing metadata that's keyed by Entrez gene identifier and whose values are + # dicts containing label, description, and synonym information. For example: + # {{'http://www.ncbi.nlm.nih.gov/gene/1': { + # 'Label': 'A1BG', + # 'Description': "A1BG is 'protein-coding' and is located on chromosome 19 (19q13.43).", + # 'Synonym': 'HEL-S-163pA|A1B|ABG|HYST2477alpha-1B-glycoprotein|GAB'}, ...} + # """ + # + # log_str = 'Generating Metadata for Gene Identifiers'; print('\t- ' + log_str); logger.info(log_str) + # + # f_name = 'Homo_sapiens.gene_info' + # x = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name, self.temp_dir) + # data = pandas.read_csv(x, header=0, delimiter='\t', low_memory=False) + # data = data.loc[data['#tax_id'].apply(lambda i: i == 9606)] + # data.fillna('None', inplace=True); data.replace('-', 'None', inplace=True, regex=False) + # # create metadata + # genes, lab, desc, syn = [], [], [], [] + # for idx, row in tqdm(data.iterrows(), total=data.shape[0]): + # gene_id, sym, defn, gene_type = row['GeneID'], row['Symbol'], row['description'], row['type_of_gene'] + # chrom, map_loc, s1, s2 = row['chromosome'], row['map_location'], row['Synonyms'], row['Other_designations'] + # if gene_id != 'None': + # genes.append('http://www.ncbi.nlm.nih.gov/gene/' + str(gene_id)) + # if sym != 'None' or sym != '': lab.append(sym) + # else: lab.append('Entrez_ID:' + gene_id) + # if 'None' not in [defn, gene_type, chrom, map_loc]: + # desc_str = "{} has locus group '{}' and is located on chromosome {} ({})." + # desc.append(desc_str.format(sym, gene_type, chrom, map_loc)) + # else: desc.append("{} locus group '{}'.".format(sym, gene_type)) + # if s1 != 'None' and s2 != 'None': + # syn.append('|'.join(set([x for x in (s1 + s2).split('|') if x != 'None' or x != '']))) + # elif s1 != 'None': syn.append('|'.join(set([x for x in s1.split('|') if x != 'None' or x != '']))) + # elif s2 != 'None': syn.append('|'.join(set([x for x in s2.split('|') if x != 'None' or x != '']))) + # else: syn.append('None') + # # combine into new data frame then convert it to dictionary + # metadata = pandas.DataFrame(list(zip(genes, lab, desc, syn)), columns=['ID', 'Label', 'Description', 'Synonym']) + # metadata = metadata.astype(str); metadata.drop_duplicates(subset='ID', inplace=True) + # metadata.set_index('ID', inplace=True); gene_metadata_dict = metadata.to_dict('index') + # + # return gene_metadata_dict + # + # def _creates_transcript_metadata_dict(self) -> Dict: + # """Creates a dictionary to store labels, synonyms, and a description for each Entrez gene identifier present + # in the input data file. + # + # Returns: + # rna_metadata_dict: A dict containing metadata that's keyed by Ensembl transcript identifier and whose values + # are a dict containing label, description, and synonym information. For example: + # {'https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=ENST00000456328': { + # 'Label': 'DDX11L1-202', + # 'Description': "Transcript DDX11L1-202 is classified as type 'processed_transcript'.", + # 'Synonym': 'None'}, ...} + # """ + # + # log_str = 'Generating Metadata for Transcript Identifiers'; print('\t- ' + log_str); logger.info(log_str) + # + # f_name = 'ensembl_identifier_data_cleaned.txt' + # x = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name, self.temp_dir) + # dup_cols = ['transcript_stable_id', 'transcript_name', 'ensembl_transcript_type'] + # data = pandas.read_csv(x, header=0, delimiter='\t', low_memory=False) + # data = data.loc[data['transcript_stable_id'].apply(lambda i: i != 'None')] + # data.drop(['ensembl_gene_id', 'symbol', 'protein_stable_id', 'uniprot_id', 'master_transcript_type', + # 'entrez_id', 'ensembl_gene_type', 'master_gene_type', 'symbol'], axis=1, inplace=True) + # data.drop_duplicates(subset=dup_cols, keep='first', inplace=True); data.fillna('None', inplace=True) + # # create metadata + # rna, lab, desc, syn = [], [], [], [] + # for idx, row in tqdm(data.iterrows(), total=data.shape[0]): + # rna_id, ent_type, nme = row[dup_cols[0]], row[dup_cols[2]], row[dup_cols[1]] + # rna.append('https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=' + rna_id) + # if nme != 'None': lab.append(nme) + # else: lab.append('Ensembl_Transcript_ID:' + rna_id); nme = 'Ensembl_Transcript_ID:' + rna_id + # if ent_type != 'None': desc.append("Transcript {} is classified as type '{}'.".format(nme, ent_type)) + # else: desc.append('None') + # syn.append('None') + # # combine into new data frame then convert it to dictionary + # metadata = pandas.DataFrame(list(zip(rna, lab, desc, syn)), columns=['ID', 'Label', 'Description', 'Synonym']) + # metadata = metadata.astype(str); metadata.drop_duplicates(subset='ID', inplace=True) + # metadata.set_index('ID', inplace=True); rna_metadata_dict = metadata.to_dict('index') + # + # return rna_metadata_dict + # + # def _creates_variant_metadata_dict(self) -> Dict: + # """Creates a dictionary to store labels, synonyms, and a description for each ClinVar variant identifier present + # in the input data file. + # + # Returns: + # variant_metadata_dict: A dict containing metadata that's keyed by ClinVar variant identifier and whose + # values are a dict containing label, description, and synonym information. For example: + # {{'https://www.ncbi.nlm.nih.gov/snp/rs141138948': { + # 'Label': 'NM_016042.4(EXOSC3):c.395A>C (p.Asp132Ala)', + # 'Description': "This variant is a germline single nucleotide variant on chromosome 9 + # (NC_000009.12, start:37783993/stop:37783993 positions,cytogenetic location:9p13.2) and + # has clinical significance 'Pathogenic/Likely pathogenic'. This entry is for the GRCh38 and was + # last reviewed on Sep 30, 2020 with review status 'criteria provided, multiple submitters, + # no conflict'.", 'Synonym': 'None'}, ...} + # """ + # + # log_str = 'Generating Metadata for Variant IDs'; print('\t- ' + log_str); logger.info(log_str) + # + # f_name = 'variant_summary.txt' + # x = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name, self.temp_dir) + # data = pandas.read_csv(x, header=0, delimiter='\t', low_memory=False) + # data = data.loc[data['Assembly'].apply(lambda i: i == 'GRCh38')] + # data = data.loc[data['RS# (dbSNP)'].apply(lambda i: i != -1)] + # data = data[['#AlleleID', 'Type', 'Name', 'ClinicalSignificance', 'RS# (dbSNP)', 'Origin', 'Start', 'Stop', + # 'ChromosomeAccession', 'Chromosome', 'ReferenceAllele', 'Assembly', 'AlternateAllele', + # 'Cytogenetic', 'ReviewStatus', 'LastEvaluated']] + # data.replace('na', 'None', inplace=True); data.fillna('None', inplace=True) + # data.sort_values('LastEvaluated', ascending=False, inplace=True) + # data.drop_duplicates(subset='RS# (dbSNP)', keep='first', inplace=True) + # # create metadata + # var, label, desc, syn = [], [], [], [] + # for idx, row in tqdm(data.iterrows(), total=data.shape[0]): + # var_id, lab = row['RS# (dbSNP)'], row['Name'] + # if var_id != 'None': + # var.append('https://www.ncbi.nlm.nih.gov/snp/rs' + str(var_id)) + # if lab != 'None': label.append(lab) + # else: label.append('dbSNP_ID:rs' + str(var_id)) + # sent = "This variant is a {} {} located on chromosome {} ({}, start:{}/stop:{} positions, " + \ + # "cytogenetic location:{}) and has clinical significance '{}'. " + \ + # "This entry is for the {} and was last reviewed on {} with review status '{}'." + # desc.append( + # sent.format(row['Origin'].str.replace(';', '/'), row['Type'].replace(';', '/'), row['Chromosome'], + # row['ChromosomeAccession'], row['Start'], row['Stop'], row['Cytogenetic'], + # row['ClinicalSignificance'], row['Assembly'], row['LastEvaluated'], + # row['ReviewStatus']).replace('None', 'UNKNOWN')) + # syn.append('None') + # # combine into new data frame then convert it to dictionary + # metadata = pandas.DataFrame(list(zip(var, label, desc, syn)), columns=['ID', 'Label', 'Description', 'Synonym']) + # metadata.drop_duplicates(inplace=True); metadata = metadata.astype(str) + # metadata.set_index('ID', inplace=True); variant_metadata_dict = metadata.to_dict('index') + # + # return variant_metadata_dict @staticmethod def _metadata_api_mapper(nodes: List[str]) -> pandas.DataFrame: @@ -1548,7 +1874,7 @@ def _creates_pathway_metadata_dict(self) -> Dict: g = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name1, self.temp_dir) data1 = pandas.read_csv(g, header=None, delimiter='\t', skiprows=4, low_memory=False) data1 = data1.loc[data1[12].apply(lambda x: x == 'taxon:9606')] - data1[5].str.replace('REACTOME:', '', inplace=True, regex=True) + data1[5] = data1[5].str.replace('REACTOME:', '', regex=True) # reactome CHEBI data f_name2 = 'ChEBI2Reactome_All_Levels.txt' h = downloads_data_from_gcs_bucket(self.bucket, self.original_data, self.processed_data, f_name2, self.temp_dir) @@ -1598,28 +1924,203 @@ def _creates_relations_metadata_dict(self) -> Dict: return relation_metadata_dict - def creates_non_ontology_class_metadata_dict(self) -> None: - """Combines the gene metadata, transcript metadata, variant metadata, pathway metadata, and relations - metadata dictionaries into a single large metadata dictionary. See example output below: - { - 'nodes': { - 'http://www.ncbi.nlm.nih.gov/gene/1': { - 'Label': 'A1BG', - 'Description': "A1BG has locus group protein-coding' and is located on chromosome 19 (19q13.43).", - 'Synonym': 'HYST2477alpha-1B-glycoprotein|HEL-S-163pA|ABG|A1B|GAB'} ... }, - 'relations': { - 'http://purl.obolibrary.org/obo/RO_0002533': { - 'Label': 'sequence atomic unit', - 'Description': 'Any individual unit of a collection of like units arranged in a linear order', - 'Synonym': 'None'} ... } + def _loads_mapping_data(self) -> Dict: + """ + + Returns: + id_map_dict: A dictionary of Pandas DataFrame objects keyed by variable name. + """ + + id_map_dict: Dict = { + 'rna_map': pandas.read_csv(self.temp_dir + '/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt', + header=None, delimiter='\t', low_memory=False, usecols=[0, 1, 2, 4], + names=['Entrez_Gene_IDs', 'Ensembl_Transcript_IDs', 'Entrez_Gene_Type', + 'Ensembl_Transcript_Type', 'Master_Gene_Type', 'Master_Transcript_Type', + 'Entrez_Gene_prefix']), + 'entrez_pro_map' : pandas.read_csv(self.temp_dir + '/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt', + header=None, delimiter='\t', low_memory=False, usecols=[0, 1, 2, 4], + names=['Gene_IDs', 'Protein_Ontology_IDs', 'Entrez_Gene_Type', + 'Master_Gene_Type', 'Entrez_Gene_Prefix']), + 'string_pro_map': pandas.read_csv(self.temp_dir + '/STRING_PRO_ONTOLOGY_MAP.txt', + header=None, delimiter='\t', low_memory=False, usecols=[0, 1], + names=['STRING_IDs', 'Protein_Ontology_IDs']), + 'uniprot_pro_map': pandas.read_csv(self.temp_dir + '/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt', + header=None, delimiter='\t', low_memory=False, usecols=[0, 1], + names=['Uniprot_Accession_IDs', 'Protein_Ontology_IDs']), + 'uniprot_entrez_data': pandas.read_csv(self.temp_dir + '/UNIPROT_ACCESSION_ENTREZ_GENE_MAP.txt', + header=None, delimiter='\t', low_memory=False, usecols=[0, 1, 2, 3], + names=['Uniprot_Accession_IDs', 'Entrez_Gene_IDs', + 'master_gene_type', 'gene_type_update']), + 'mesh_chebi_map': pandas.read_csv(self.temp_dir + '/MESH_CHEBI_MAP.txt', header=None, + names=['MESH_ID', 'CHEBI_ID'], delimiter='\t'), + 'disease_maps': pandas.read_csv(self.temp_dir + '/DISEASE_MONDO_MAP.txt', header=None, + names=['Disease_IDs', 'MONDO_IDs'], delimiter='\t'), + 'phenotype_maps': pandas.read_csv(self.temp_dir + '/PHENOTYPE_HPO_MAP.txt', header=None, + names=['Disease_IDs', 'HP_IDs'], delimiter='\t') } + return id_map_dict + + def _creates_genomic_metadata_dict(self) -> Dict: + """Process a genomic metadata dictionary created in the prior steps in order to assist with creating a master + metadata file for all nodes that are a genomic entity (i.e., genes, transcripts, or proteins). + + Returns: + genomic_metadata: A nested dictionary of genomic metadata keyed by NCBIGene, ensembl, and Protein Ontology + identifiers. + """ + + filepath = self.temp_dir + '/Merged_gene_rna_protein_identifiers.pkl' + max_bytes = 2**31 - 1; input_size = os.path.getsize(filepath); bytes_in = bytearray(0) + with open(filepath, 'rb') as f_in: + for _ in range(0, input_size, max_bytes): + bytes_in += f_in.read(max_bytes) + reformatted_mapped_identifiers = pickle.loads(bytes_in) + + # clean up data for use with master metadata + genomic_metadata = dict() + for key, value in tqdm(reformatted_mapped_identifiers.items()): + old_prefix = '_'.join(key.split('_')[0:-1]); idx = key.split('_')[-1]; pass_var = True; new_prefix = None + if old_prefix == 'entrez_id': new_prefix = 'NCBIGene' + elif old_prefix in ['ensembl_gene_id', 'protein_stable_id', 'transcript_stable_id']: new_prefix = 'ensembl' + elif old_prefix == 'pro_id_PR': new_prefix = 'PR' + else: pass_var = False + if pass_var and new_prefix is not None: + updated_key = new_prefix + '_' + idx; master_metadata_dict = {updated_key: {}} + for x in value: + i, j = '_'.join(x.split('_')[0:-1]), x.split('_')[-1] + if 'type' in i: continue + elif i == 'entrez_id': new_i = 'NCBIGene'; j = new_i + '_' + j + elif i == 'ensembl_gene_id': new_i = 'ensembl gene'; j = 'ensembl_' + j + elif i == 'protein_stable_id': new_i = 'ensembl protein'; j = 'ensembl_' + j + elif i == 'transcript_stable_id': new_i = 'ensembl transcript'; j = 'ensembl_' + j + elif i == 'pro_id_PR': new_i = 'PR'; j = new_i + '_' + j + elif i == 'hgnc_id': new_i = 'HGNC_ID'; j = new_i + '_' + j + elif i == 'uniprot_id': new_i = 'uniprot'; j = new_i + '_' + j + elif i == 'symbol': new_i = 'GeneSymbol'; j = new_i + '_' + j + else: + if i == 'synonyms': new_i = 'Synonyms' + elif i == 'name': new_i = 'Label' + elif i == 'Other_designations': new_i = 'Synonyms'; j = j.split('|') + else: new_i = i + if new_i in master_metadata_dict[updated_key].keys(): + if isinstance(j, list): master_metadata_dict[updated_key][new_i] += j + else: master_metadata_dict[updated_key][new_i] += [j] + else: master_metadata_dict[updated_key][new_i] = [j] + genomic_metadata[updated_key] = master_metadata_dict + + return genomic_metadata + + def _processes_ctd_gene_inx_data(self, master, g_dict, mesh_chebi_map, rna_map, entrez_pro_map) -> Dict: + """This function processes the CTD_chem_gene_ixns.tsv file and obtains the following node and edge metadata: + Nodes: + - ChemicalID: A string containing the concept's MESH identifier. + - CasRN: A string containing a CAS Registry Number. + - ChemicalName: A string containing the concept's synonym. + - Organism: A string containing the name of an organism. + - GeneSymbol: A string containing the concept's gene symbol. + Relations: + - Interaction: A string describing a chemical-gene/protein/rna interaction. + - InteractionActions: A "|"-delimited list of the actions that underlie an interaction. + - PubMedIDs: |'-delimited list of PubMed identifiers that do not include a prefix. + + Args: + master: The master metadata dictionary keyed by nodes and relations. + g_dict: A nested dictionary of genomic metadata keyed by NCBIGene, ensembl, and Protein Ontology IDs. + mesh_chebi_map: A Pandas DataFrame that contains MeSH-CHEBI identifier mappings. + rna_map: A Pandas DataFrame that contains Entrez Gene-Ensembl Transcript identifier mappings. + entrez_pro_map: A Pandas DataFrame that contains Entrez Gene-Protein Ontology identifier mappings. + + Returns: + master_dict: The master metadata dictionary keyed by nodes and relations. + """ + + # download and process data + url = 'http://ctdbase.org/reports/CTD_chem_gene_ixns.tsv.gz'; f_name = self.temp_dir + '/CTD_chem_gene_ixns.tsv' + if not os.path.exists(f_name): data_downloader(url, f_name) + df = pandas.read_csv(f_name, header=0, delimiter='\t', skiprows=27) + df = df[df['# ChemicalName'] != '#']; df = df[df['OrganismID'] == 9606]; df = df[df['PubMedIDs'] != numpy.nan] + df['ChemicalID'] = 'MESH:' + df['ChemicalID'] + df['GeneID'] = df['GeneID'].astype('Int64'); df['OrganismID'] = df['OrganismID'].astype('Int64') + # merge identifier maps + df = df.merge(mesh_chebi_map, left_on='ChemicalID', right_on='MESH_ID') + df = df.merge(rna_map, left_on='GeneID', right_on='Entrez_Gene_IDs') + df = df.merge(entrez_pro_map, left_on='GeneID', right_on='Gene_IDs') + + for idx, row in tqdm(df.iterrows(), total=df.shape[0]): + chebi = row['CHEBI_ID'].rstrip(); n_key = None; genomic_info = None; r_key = None; form = None + chemical_name = row['# ChemicalName']; chemical_id = row['ChemicalID'].rstrip(); casrn = row['CasRN'] + evidence = [{'CTD_Interaction': row['Interaction'], 'CTD_InteractionActions': row['InteractionActions'], + 'CTD_PubMedIDs': row['PubMedIDs']}] + g_info = None; relation_key = '{}-{}'.format(chebi, n_key); edge_type = None + if row['GeneForms'] == 'gene': + n_key = row['Entrez_Gene_prefix'].rstrip(); edge_type = 'chemical-gene'; form = row['GeneForms'] + if n_key in g_dict.keys(): g_info = g_dict[n_key] + if row['GeneForms'] == 'protein': + n_key = row['Protein_Ontology_IDs'].rstrip(); edge_type = 'chemical-protein'; form = row['GeneForms'] + if n_key in g_dict.keys(): g_info = g_dict[n_key] + if row['GeneForms'] == 'rna': + n_key = row['Ensembl_Transcript_IDs'].rstrip(); edge_type = 'chemical-rna'; form = row['GeneForms'] + if n_key in g_dict.keys(): g_info = g_dict[n_key] + if form is not None: + # add node data to dictionary + if chebi in master['nodes'].keys(): + if 'CTD_ChemicalName' in master['nodes'][chebi].keys(): + master['nodes'][chebi]['CTD_ChemicalName'] |= {chemical_name} + else: master['nodes'][chebi]['CTD_ChemicalName'] = {chemical_name} + if 'CTD_ChemicalID' in master['nodes'][chebi].keys(): + master['nodes'][chebi]['ChemicalID'] |= {chemical_id} + else: master['nodes'][chebi]['ChemicalID'] = {chemical_id} + if 'CTD_CasRN' in master['nodes'][chebi].keys(): master['nodes'][chebi]['CTD_CasRN'] |= {casrn} + else: master['nodes'][chebi]['CTD_CasRN'] = {casrn} + else: + master['nodes'][n_key] = {'CTD_GeneForms': form} + if g_info is not None: master['nodes'][n_key]['genomic_data'] = {n_key: g_info} + master['nodes'][chebi] = {} + master['nodes'][chebi]['ChemicalID'] = {chemical_id} + master['nodes'][chebi]['CTD_CasRN'] = {casrn} + master['nodes'][chebi]['CTD_ChemicalName'] = {chemical_name} + # add relation data to dictionary + if r_key in master['relations'][edge_type].keys(): + if 'CTD_Evidence' in master['relations'][edge_type][r_key].keys(): + master['relations'][edge_type][r_key]['CTD_Evidence'] += [evidence] + else: master['relations'][edge_type][r_key]['CTD_Evidence'] = [evidence] + else: + master['relations'][edge_type][r_key] = {} + master['relations'][edge_type][r_key]['CTD_Evidence'] = [evidence] + + return master + + + + def creates_metadata_dict(self) -> None: + """Creates a single large metadata dictionary that is keyed by nodes and relations and contains a variety of + metadata. See the following file for additional details: + https://github.com/callahantiff/PheKnowLator/tree/master/resources/pheknowlator_source_metadata.xlsx. + Returns: None. """ log_str = 'Creating Master Metadata Dictionary for Non-Ontology Entities'; print(log_str); logger.info(log_str) + # load identifier mapping data + id_map = self._loads_mapping_data() + rna_map = id_map['rna_map']; entrez_pro_map = id_map['entrez_pro_map']; disease_maps = id_map['disease_maps'] + string_pro_map = id_map['string_pro_map']; uniprot_pro_map = id_map['uniprot_pro_map'] + uniprot_entrez_data = id_map['uniprot_entrez_data']; mesh_chebi_map = id_map['mesh_chebi_map'] + phenotype_maps = id_map['phenotype_maps'] + + # create dictionary + master_dict = {'nodes': {}, 'relations': {}} + + # obtain metadata dictionaries + genomic = self._creates_genomic_metadata_dict() + master_dict = self._processes_ctd_gene_inx_data(master_dict, genomic, mesh_chebi_map, rna_map, entrez_pro_map) + + + + # create single dictionary of master_metadata_dictionary = {'nodes': {**self._creates_gene_metadata_dict(), **self._creates_transcript_metadata_dict(), @@ -1702,7 +2203,7 @@ def preprocesses_build_data(self) -> None: # STEP 10: Non-Ontology Metadata Dictionary log_str = 'STEP 10: CREATING OBO-ONTOLOGY METADATA DICTIONARY'; print('\n' + log_str); logger.info(log_str) - self.creates_non_ontology_class_metadata_dict() + self.creates_metadata_dict() uploads_data_to_gcs_bucket(self.bucket, self.log_location, log_dir, log) return None From 57dbf85211507b0fb0badbbe43936c9661b186e6 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:17:34 -0400 Subject: [PATCH 091/112] light overhaul --- notebooks/Data_Preparation.ipynb | 768 ++++++++++++++++++++----------- 1 file changed, 510 insertions(+), 258 deletions(-) diff --git a/notebooks/Data_Preparation.ipynb b/notebooks/Data_Preparation.ipynb index 602631ae..75557f65 100644 --- a/notebooks/Data_Preparation.ipynb +++ b/notebooks/Data_Preparation.ipynb @@ -16,11 +16,11 @@ "\n", "**Author:** [TJCallahan](https://mail.google.com/mail/u/0/?view=cm&fs=1&tf=1&to=callahantiff@gmail.com) \n", "**GitHub Repository:** [PheKnowLator](https://github.com/callahantiff/PheKnowLator/wiki) \n", - "**Release:** **`v4.0.0`**\n", + "**Release:** **[`v4.0.0`](https://github.com/callahantiff/PheKnowLator/wiki/v4.0.0)**\n", " \n", "<br> \n", " \n", - "**Purpose:** This notebook serves as a script to download and process data in order to generate mapping and filtering data needed to build edges for the PheKnowLator knowledge graph. For more information on the data sources utilize within this script, please see the [Data Sources](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources) Wiki page.\n", + "**Purpose:** This notebook serves as a script to download and process data in order to generate mapping and filtering data needed to build edges for the PheKnowLator knowledge graph. For more information on the data sources utilize within this script, please see the [Data Sources](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources) Wiki page.\n", "\n", "<br>\n", "\n", @@ -32,7 +32,7 @@ "\n", "**Dependencies:** \n", "- **Scripts**: This notebook utilizes several helper functions, which are stored in the [`data_utils.py`](https://github.com/callahantiff/PheKnowLator/blob/master/pkt_kg/utils/data_utils.py) and [`kg_utils.py`](https://github.com/callahantiff/PheKnowLator/blob/master/pkt_kg/utils/kg_utils.py) scripts. \n", - "- **Data**: Hyperlinks to all downloaded and generated data sources are provided through [this](https://console.cloud.google.com/storage/browser/pheknowlator/release_v2.0.0?project=pheknowlator) dedicated Google Cloud Storage Bucket. <u>This notebook will download everything that is needed for you</u>. \n", + "- **Data**: Hyperlinks to all downloaded and generated data sources are provided through [this](https://console.cloud.google.com/storage/browser/pheknowlator/release_v4.0.0?project=pheknowlator) dedicated Google Cloud Storage Bucket. <u>This notebook will download everything that is needed for you</u>. \n", "_____\n", "***" ] @@ -123,7 +123,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -134,7 +134,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -151,6 +151,7 @@ "import pickle\n", "import re\n", "import requests\n", + "import shutil\n", "import sys\n", "\n", "from collections import Counter\n", @@ -158,7 +159,7 @@ "from rdflib import Graph, Namespace, URIRef, BNode, Literal\n", "from rdflib.namespace import OWL, RDF, RDFS\n", "from reactome2py import content\n", - "from tqdm import tqdm\n", + "from tqdm.notebook import tqdm\n", "from typing import Dict\n", "\n", "from pkt_kg.utils import * # import pkt_kg utility script containing helper functions" @@ -173,7 +174,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -227,10 +228,10 @@ "\n", "**Data Source Wiki Pages:** \n", "- [Ensembl](https://uswest.ensembl.org/) \n", - "- [Uniprot Knowledgebase](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#uniprot-knowledgebase) \n", + "- [Uniprot Knowledgebase](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#universal-protein-resource-knowledgebase) \n", "- [HGNC](ftp://ftp.ebi.ac.uk/pub/databases/genenames/new/tsv/hgnc_complete_set.txt) \n", - "- [NCBI Gene](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#ncbi-gene) \n", - "- [Protein Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#protein-ontology)\n", + "- [NCBI Gene](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#national-center-for-biotechnology-information-gene) \n", + "- [Protein Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources/#protein-ontology)\n", "\n", "<br>\n", "\n", @@ -1293,7 +1294,7 @@ "\n", "The URL to access the results of this query is obtained by clicking on the share symbol and copying the free-text from the box. To obtain the data in a tab-delimited format the following string is appended to the end of the URL: \"&format=tab\".\n", "\n", - "**NOTE.** Be sure to obtain a new URL from the [UniProt Knowledgebase](https://www.uniprot.org/uniprot/) when rebuilding to ensure you are getting the most up-to-date data. This query was last generated on `12/02/2020`." + "**NOTE.** Be sure to obtain a new URL from the [UniProt Knowledgebase](https://www.uniprot.org/uniprot/) when rebuilding to ensure you are getting the most up-to-date data. This query was last generated on `01/30/2022`." ] }, { @@ -1739,9 +1740,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { - "code_folding": [] + "code_folding": [ + 0 + ] }, "outputs": [], "source": [ @@ -2164,7 +2167,7 @@ "source": [ "### ChEBI-MeSH Identifiers <a class=\"anchor\" id=\"mesh-chebi\"></a>\n", "\n", - "**Data Source Wiki Page:** [mapping-mesh-to-chebi](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#mapping-mesh-identifiers-to-chebi-identifiers) \n", + "**Data Source Wiki Page:** [mapping-mesh-to-chebi](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#mapping-mesh-identifiers-to-chebi-identifiers) \n", "\n", "**Purpose:** Map MeSH identifiers to ChEBI identifiers when creating the following edges: \n", "- chemical-gene \n", @@ -2354,8 +2357,8 @@ "### Disease and Phenotype Identifiers <a class=\"anchor\" id=\"disease-identifiers\"></a>\n", "\n", "**Data Source Wiki Page:** \n", - "- [DisGeNET](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#disgenet) \n", - "- [MedGen](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#ncbi-medgen) \n", + "- [DisGeNET](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#disgenet) \n", + "- [MedGen](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#national-center-for-biotechnology-information-medgen) \n", "\n", "**Purpose:** This script downloads the Human Phenotype Ontology (HPO), the MonDO Disease Ontology (MONDO), [disease_mappings.tsv](https://www.disgenet.org/static/disgenet_ap1/files/downloads/disease_mappings.tsv.gz), and [MGCONSO.RRF](https://ftp.ncbi.nlm.nih.gov/pub/medgen/MGCONSO.RRF.gz) in order to map UMLS identifiers to HPO and MONDO identifiers when creating the following edges: \n", "- chemical-disease \n", @@ -2617,7 +2620,7 @@ "metadata": {}, "source": [ "_Build Disease Identifier Dictionary_ \n", - "In order to improve efficiency when mapping different disease terminology identifiers to the [MonDO Disease Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#mondo-disease-ontology) and [Human Phenotype Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#human-phenotype-ontology), we create a dictionary of disease identifiers." + "In order to improve efficiency when mapping different disease terminology identifiers to the [MonDO Disease Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#mondo-disease-ontology) and [Human Phenotype Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#human-phenotype-ontology), we create a dictionary of disease identifiers." ] }, { @@ -2738,12 +2741,12 @@ "### Human Protein Atlas/GTEx Tissue/Cells - UBERON + Cell Ontology + Cell Line Ontology <a class=\"anchor\" id=\"hpa-uberon\"></a>\n", "\n", "**Data Source Wiki Page:** \n", - "- [human-protein-atlas](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#human-protein-atlas) \n", - "- [genotype-tissue-expression-project](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#the-genotype-tissue-expression-gtex-project) \n", + "- [human-protein-atlas](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#human-protein-atlas) \n", + "- [genotype-tissue-expression-project](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#genotype-tissue-expression-project) \n", "\n", "<br>\n", "\n", - "**Purpose:** Downloads a query for cell, tissue, and blood types with overexpressed protein-coding genes in the human proteome ([`proteinatlas_search.tsv`](https://www.proteinatlas.org/api/search_download.php?search=&columns=g,eg,up,pe,rnatsm,rnaclsm,rnacasm,rnabrsm,rnabcsm,rnablsm,scl,t_RNA_adipose_tissue,t_RNA_adrenal_gland,t_RNA_amygdala,t_RNA_appendix,t_RNA_basal_ganglia,t_RNA_bone_marrow,t_RNA_breast,t_RNA_cerebellum,t_RNA_cerebral_cortex,t_RNA_cervix,_uterine,t_RNA_colon,t_RNA_corpus_callosum,t_RNA_ductus_deferens,t_RNA_duodenum,t_RNA_endometrium_1,t_RNA_epididymis,t_RNA_esophagus,t_RNA_fallopian_tube,t_RNA_gallbladder,t_RNA_heart_muscle,t_RNA_hippocampal_formation,t_RNA_hypothalamus,t_RNA_kidney,t_RNA_liver,t_RNA_lung,t_RNA_lymph_node,t_RNA_midbrain,t_RNA_olfactory_region,t_RNA_ovary,t_RNA_pancreas,t_RNA_parathyroid_gland,t_RNA_pituitary_gland,t_RNA_placenta,t_RNA_pons_and_medulla,t_RNA_prostate,t_RNA_rectum,t_RNA_retina,t_RNA_salivary_gland,t_RNA_seminal_vesicle,t_RNA_skeletal_muscle,t_RNA_skin_1,t_RNA_small_intestine,t_RNA_smooth_muscle,t_RNA_spinal_cord,t_RNA_spleen,t_RNA_stomach_1,t_RNA_testis,t_RNA_thalamus,t_RNA_thymus,t_RNA_thyroid_gland,t_RNA_tongue,t_RNA_tonsil,t_RNA_urinary_bladder,t_RNA_vagina,t_RNA_B-cells,t_RNA_dendritic_cells,t_RNA_granulocytes,t_RNA_monocytes,t_RNA_NK-cells,t_RNA_T-cells,t_RNA_total_PBMC,cell_RNA_A-431,cell_RNA_A549,cell_RNA_AF22,cell_RNA_AN3-CA,cell_RNA_ASC_diff,cell_RNA_ASC_TERT1,cell_RNA_BEWO,cell_RNA_BJ,cell_RNA_BJ_hTERT+,cell_RNA_BJ_hTERT+_SV40_Large_T+,cell_RNA_BJ_hTERT+_SV40_Large_T+_RasG12V,cell_RNA_CACO-2,cell_RNA_CAPAN-2,cell_RNA_Daudi,cell_RNA_EFO-21,cell_RNA_fHDF/TERT166,cell_RNA_HaCaT,cell_RNA_HAP1,cell_RNA_HBEC3-KT,cell_RNA_HBF_TERT88,cell_RNA_HDLM-2,cell_RNA_HEK_293,cell_RNA_HEL,cell_RNA_HeLa,cell_RNA_Hep_G2,cell_RNA_HHSteC,cell_RNA_HL-60,cell_RNA_HMC-1,cell_RNA_HSkMC,cell_RNA_hTCEpi,cell_RNA_hTEC/SVTERT24-B,cell_RNA_hTERT-HME1,cell_RNA_HUVEC_TERT2,cell_RNA_K-562,cell_RNA_Karpas-707,cell_RNA_LHCN-M2,cell_RNA_MCF7,cell_RNA_MOLT-4,cell_RNA_NB-4,cell_RNA_NTERA-2,cell_RNA_PC-3,cell_RNA_REH,cell_RNA_RH-30,cell_RNA_RPMI-8226,cell_RNA_RPTEC_TERT1,cell_RNA_RT4,cell_RNA_SCLC-21H,cell_RNA_SH-SY5Y,cell_RNA_SiHa,cell_RNA_SK-BR-3,cell_RNA_SK-MEL-30,cell_RNA_T-47d,cell_RNA_THP-1,cell_RNA_TIME,cell_RNA_U-138_MG,cell_RNA_U-2_OS,cell_RNA_U-2197,cell_RNA_U-251_MG,cell_RNA_U-266/70,cell_RNA_U-266/84,cell_RNA_U-698,cell_RNA_U-87_MG,cell_RNA_U-937,cell_RNA_WM-115,blood_RNA_basophil,blood_RNA_classical_monocyte,blood_RNA_eosinophil,blood_RNA_gdT-cell,blood_RNA_intermediate_monocyte,blood_RNA_MAIT_T-cell,blood_RNA_memory_B-cell,blood_RNA_memory_CD4_T-cell,blood_RNA_memory_CD8_T-cell,blood_RNA_myeloid_DC,blood_RNA_naive_B-cell,blood_RNA_naive_CD4_T-cell,blood_RNA_naive_CD8_T-cell,blood_RNA_neutrophil,blood_RNA_NK-cell,blood_RNA_non-classical_monocyte,blood_RNA_plasmacytoid_DC,blood_RNA_T-reg,blood_RNA_total_PBMC,brain_RNA_amygdala,brain_RNA_basal_ganglia,brain_RNA_cerebellum,brain_RNA_cerebral_cortex,brain_RNA_hippocampal_formation,brain_RNA_hypothalamus,brain_RNA_midbrain,brain_RNA_olfactory_region,brain_RNA_pons_and_medulla,brain_RNA_thalamus&format=tsv)) via [API](https://www.proteinatlas.org/about/help/dataaccess) and median gene-level TPM by tissue for all genes that are not protein-coding ([`GTEx_Analysis_2017-06-05_v8_RNASeQCv1.1.9_gene_median_tpm.gct`](https://storage.googleapis.com/gtex_analysis_v8/rna_seq_data/GTEx_Analysis_2017-06-05_v8_RNASeQCv1.1.9_gene_median_tpm.gct.gz)) in order to create mappings between cell and tissue type strings to the Uber-Anatomy, Cell Ontology, and Cell Line Ontology concepts (see [human-protein-atlas](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#human-protein-atlas) for details on the mapping process). The mappings are then used to create the following edge types: \n", + "**Purpose:** Downloads a query for cell, tissue, and blood types with overexpressed protein-coding genes in the human proteome ([`proteinatlas_search.tsv`](https://www.proteinatlas.org/api/search_download.php?search=&columns=g,eg,up,pe,rnatsm,rnaclsm,rnacasm,rnabrsm,rnabcsm,rnablsm,scl,t_RNA_adipose_tissue,t_RNA_adrenal_gland,t_RNA_amygdala,t_RNA_appendix,t_RNA_basal_ganglia,t_RNA_bone_marrow,t_RNA_breast,t_RNA_cerebellum,t_RNA_cerebral_cortex,t_RNA_cervix,_uterine,t_RNA_colon,t_RNA_corpus_callosum,t_RNA_ductus_deferens,t_RNA_duodenum,t_RNA_endometrium_1,t_RNA_epididymis,t_RNA_esophagus,t_RNA_fallopian_tube,t_RNA_gallbladder,t_RNA_heart_muscle,t_RNA_hippocampal_formation,t_RNA_hypothalamus,t_RNA_kidney,t_RNA_liver,t_RNA_lung,t_RNA_lymph_node,t_RNA_midbrain,t_RNA_olfactory_region,t_RNA_ovary,t_RNA_pancreas,t_RNA_parathyroid_gland,t_RNA_pituitary_gland,t_RNA_placenta,t_RNA_pons_and_medulla,t_RNA_prostate,t_RNA_rectum,t_RNA_retina,t_RNA_salivary_gland,t_RNA_seminal_vesicle,t_RNA_skeletal_muscle,t_RNA_skin_1,t_RNA_small_intestine,t_RNA_smooth_muscle,t_RNA_spinal_cord,t_RNA_spleen,t_RNA_stomach_1,t_RNA_testis,t_RNA_thalamus,t_RNA_thymus,t_RNA_thyroid_gland,t_RNA_tongue,t_RNA_tonsil,t_RNA_urinary_bladder,t_RNA_vagina,t_RNA_B-cells,t_RNA_dendritic_cells,t_RNA_granulocytes,t_RNA_monocytes,t_RNA_NK-cells,t_RNA_T-cells,t_RNA_total_PBMC,cell_RNA_A-431,cell_RNA_A549,cell_RNA_AF22,cell_RNA_AN3-CA,cell_RNA_ASC_diff,cell_RNA_ASC_TERT1,cell_RNA_BEWO,cell_RNA_BJ,cell_RNA_BJ_hTERT+,cell_RNA_BJ_hTERT+_SV40_Large_T+,cell_RNA_BJ_hTERT+_SV40_Large_T+_RasG12V,cell_RNA_CACO-2,cell_RNA_CAPAN-2,cell_RNA_Daudi,cell_RNA_EFO-21,cell_RNA_fHDF/TERT166,cell_RNA_HaCaT,cell_RNA_HAP1,cell_RNA_HBEC3-KT,cell_RNA_HBF_TERT88,cell_RNA_HDLM-2,cell_RNA_HEK_293,cell_RNA_HEL,cell_RNA_HeLa,cell_RNA_Hep_G2,cell_RNA_HHSteC,cell_RNA_HL-60,cell_RNA_HMC-1,cell_RNA_HSkMC,cell_RNA_hTCEpi,cell_RNA_hTEC/SVTERT24-B,cell_RNA_hTERT-HME1,cell_RNA_HUVEC_TERT2,cell_RNA_K-562,cell_RNA_Karpas-707,cell_RNA_LHCN-M2,cell_RNA_MCF7,cell_RNA_MOLT-4,cell_RNA_NB-4,cell_RNA_NTERA-2,cell_RNA_PC-3,cell_RNA_REH,cell_RNA_RH-30,cell_RNA_RPMI-8226,cell_RNA_RPTEC_TERT1,cell_RNA_RT4,cell_RNA_SCLC-21H,cell_RNA_SH-SY5Y,cell_RNA_SiHa,cell_RNA_SK-BR-3,cell_RNA_SK-MEL-30,cell_RNA_T-47d,cell_RNA_THP-1,cell_RNA_TIME,cell_RNA_U-138_MG,cell_RNA_U-2_OS,cell_RNA_U-2197,cell_RNA_U-251_MG,cell_RNA_U-266/70,cell_RNA_U-266/84,cell_RNA_U-698,cell_RNA_U-87_MG,cell_RNA_U-937,cell_RNA_WM-115,blood_RNA_basophil,blood_RNA_classical_monocyte,blood_RNA_eosinophil,blood_RNA_gdT-cell,blood_RNA_intermediate_monocyte,blood_RNA_MAIT_T-cell,blood_RNA_memory_B-cell,blood_RNA_memory_CD4_T-cell,blood_RNA_memory_CD8_T-cell,blood_RNA_myeloid_DC,blood_RNA_naive_B-cell,blood_RNA_naive_CD4_T-cell,blood_RNA_naive_CD8_T-cell,blood_RNA_neutrophil,blood_RNA_NK-cell,blood_RNA_non-classical_monocyte,blood_RNA_plasmacytoid_DC,blood_RNA_T-reg,blood_RNA_total_PBMC,brain_RNA_amygdala,brain_RNA_basal_ganglia,brain_RNA_cerebellum,brain_RNA_cerebral_cortex,brain_RNA_hippocampal_formation,brain_RNA_hypothalamus,brain_RNA_midbrain,brain_RNA_olfactory_region,brain_RNA_pons_and_medulla,brain_RNA_thalamus&format=tsv)) via [API](https://www.proteinatlas.org/about/help/dataaccess) and median gene-level TPM by tissue for all genes that are not protein-coding ([`GTEx_Analysis_2017-06-05_v8_RNASeQCv1.1.9_gene_median_tpm.gct`](https://storage.googleapis.com/gtex_analysis_v8/rna_seq_data/GTEx_Analysis_2017-06-05_v8_RNASeQCv1.1.9_gene_median_tpm.gct.gz)) in order to create mappings between cell and tissue type strings to the Uber-Anatomy, Cell Ontology, and Cell Line Ontology concepts (see [human-protein-atlas](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#human-protein-atlas) for details on the mapping process). The mappings are then used to create the following edge types: \n", "- rna-cell line \n", "- rna-tissue type \n", "- protein-cell line \n", @@ -2801,7 +2804,7 @@ "source": [ "***\n", "**Genotype-Tissue Expression Project** \n", - "Import the tissues, cells, cell lines, and fluids that we externally mapped from HPA and GTEx data to [UBERON](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#uber-anatomy-ontology), the [Cell Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#cell-ontology), and the [Cell Line Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#cell-line-ontology)." + "Import the tissues, cells, cell lines, and fluids that we externally mapped from HPA and GTEx data to [UBERON](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#uber-anatomy-ontology), the [Cell Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#cell-ontology), and the [Cell Line Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#cell-line-ontology)." ] }, { @@ -3031,7 +3034,7 @@ "\n", "### Mapping Reactome Pathways to the Pathway Ontology <a class=\"anchor\" id=\"reactome-pw\"></a>\n", "\n", - "**Data Source Wiki Page:** [Pathway Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#pathway-ontology) \n", + "**Data Source Wiki Page:** [Pathway Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#pathway-ontology) \n", "\n", "**Purpose:** This script downloads the [canonical pathways](http://compath.scai.fraunhofer.de/export_mappings) and [kegg-reactome pathway mappings](https://github.com/ComPath/resources/blob/master/mappings/kegg_reactome.csv) files from the [ComPath Ecosystem](https://github.com/ComPath) in order to create the following identifier mappings: \n", "- `Reactome Pathway Identifiers` ➞ `KEGG Pathway Identifiers` ➞ `Pathway Ontology Identifiers` \n", @@ -3371,7 +3374,7 @@ "\n", "### Mapping Genomic Identifiers to the Sequence Ontology <a class=\"anchor\" id=\"genomic-soo\"></a>\n", "\n", - "**Data Source Wiki Page:** [Sequence Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/_edit#sequence-ontology) \n", + "**Data Source Wiki Page:** [Sequence Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources/_edit#sequence-ontology) \n", "\n", "**Purpose:** This script downloads the `genomic_sequence_ontology_mappings.xlsx` file in order to create the following identifier mappings: \n", "- `Gene BioTypes` ➞ `Sequence Ontology Identifiers` \n", @@ -3599,7 +3602,7 @@ "***\n", "### Protein Ontology <a class=\"anchor\" id=\"protein-ontology\"></a>\n", "\n", - "**Data Source Wiki Page:** [protein-ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#human-phenotype-ontology) \n", + "**Data Source Wiki Page:** [protein-ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#human-phenotype-ontology) \n", "\n", "**Purpose:** This script uses [OWLTools](https://github.com/owlcollab/owltools) to download the [pr.owl](http://purl.obolibrary.org/obo/pr.owl) (with imports) file from [ProConsortium.org](https://proconsortium.org/) in order to create a version of the ontology that contains only human proteins. This is achieved by performing forward and reverse breadth first search over all proteins which are `owl:subClassOf` [Homo sapiens protein](https://proconsortium.org/app/entry/PR%3A000029067/).\n", "\n", @@ -3835,7 +3838,7 @@ "\n", "### Relations Ontology <a class=\"anchor\" id=\"relations-ontology\"></a>\n", "\n", - "**Data Source Wiki Page:** [RO](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#relation-ontology) \n", + "**Data Source Wiki Page:** [Relations Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#relations-ontology) \n", "\n", "**Purpose:** This script downloads the [ro.owl](http://purl.obolibrary.org/obo/ro.owl) file from [obofoundry.org](http://www.obofoundry.org/) in order to obtain all `ObjectProperties` and their inverse relations. \n", "\n", @@ -3976,7 +3979,7 @@ "***\n", "### Clinvar Variant-Diseases and Phenotypes <a class=\"anchor\" id=\"clinvar-variant\"></a>\n", "\n", - "**Data Source Wiki Page:** [Clinvar](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#clinvar) \n", + "**Data Source Wiki Page:** [Clinvar](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#clinvar) \n", "\n", "**Purpose:** This script downloads the data files list below in order to create the following edges: \n", "- gene-variant \n", @@ -4515,9 +4518,9 @@ "\n", "### Uniprot Protein-Cofactor and Protein-Catalyst <a class=\"anchor\" id=\"uniprot-protein-cofactorcatalyst\"></a>\n", "\n", - "**Data Source Wiki Page:** [Uniprot](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#uniprot-knowledgebase) \n", + "**Data Source Wiki Page:** [UniProt](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#universal-protein-resource-knowledgebase) \n", "\n", - "**Purpose:** This script downloads the [uniprot-cofactor-catalyst.tab](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources/#uniprot-knowledgebase) file from the [Uniprot Knowledge Base](https://www.uniprot.org) in order to create the following edges: \n", + "**Purpose:** This script downloads the [uniprot-cofactor-catalyst.tab](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#universal-protein-resource-knowledgebase) file from the [Uniprot Knowledge Base](https://www.uniprot.org) in order to create the following edges: \n", "- protein-cofactor \n", "- protein-catalyst \n", "\n", @@ -4637,7 +4640,7 @@ "### NODE AND RELATION METADATA<a class=\"anchor\" id=\"node-relation-metadata\"></a>\n", "***\n", "\n", - "**Data Source Wiki Page:** [Dependencies](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies/#node-metadata) \n", + "**Data Source Wiki Page:** [Dependencies](https://github.com/callahantiff/PheKnowLator/wiki/Dependencies/#metadata) \n", "\n", "**Purpose:** The goal of this section is to obtain metadata for each entity that is not from an ontology and all relations used in the knowledge graph. \n", "\n", @@ -4774,12 +4777,20 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# create the shell for the node and relation dictionary\n", - "master_metadata_dictionary = {'nodes': {}, 'relations': {}, 'edges': {}}" + "master_metadata_dictionary = {'nodes': {}, 'relations': {}, 'edges': {}}\n", + "\n", + "# # create temp metadata directory\n", + "# temp_location = metadata_location + 'temp'\n", + "# if os.path.exists(temp_location): shutil.rmtree(temp_location)\n", + "# os.mkdir(temp_location)\n", + "# os.mkdir(temp_location + '/nodes')\n", + "# os.mkdir(temp_location + '/relations')\n", + "# os.mkdir(temp_location + '/edges')" ] }, { @@ -4800,7 +4811,6 @@ "metadata": {}, "source": [ "#### Primary Metadata Elements<a class=\"anchor\" id=\"primary\"></a> \n", - "\n", "***" ] }, @@ -4808,14 +4818,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "##### Genes Metadata Dictionary <a class=\"anchor\" id=\"gene-metadata\"></a>\n", + "##### Genes Metadata Dictionary <a class=\"anchor\" id=\"gene-metadata\"></a> \n", + "\n", + "**Data Source Wiki Page:** [National Center for Biotechnology Information Gene](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#national-center-for-biotechnology-information-gene)\n", "\n", - "The nested dictionary of gene metadata is created by looping over the merged data described in the prior column. The `keys` of the dictionary are `Entrez gene identifiers` and the `values` are dictionaries for each metadata type." + "The nested dictionary of gene metadata is created by looping over the cleaned human [National Center for Biotechnology Information Gene](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#national-center-for-biotechnology-information-gene) identifier data set ([`ensembl_identifier_data_cleaned.txt`](ftp://ftp.ncbi.nih.gov/gene/DATA/GENE_INFO/Mammalia/Homo_sapiens.gene_info.gz)). The `keys` of the dictionary are `Entrez gene identifiers` and the `values` are dictionaries for each metadata type." ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -4827,30 +4839,25 @@ "\n", "# replace NaN and '-' with 'None'\n", "entrez_gene_data.fillna('None', inplace=True)\n", - "entrez_gene_data.replace('-','None', inplace=True, regex=False)" + "entrez_gene_data.replace('-','None', inplace=True, regex=False)\n", + "\n", + "# update prefixes\n", + "entrez_gene_data['GeneID'] = 'NCBIGene_' + entrez_gene_data['GeneID'].astype('str')" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 65015/65015 [00:20<00:00, 3222.04it/s]\n" - ] - } - ], + "outputs": [], "source": [ "# create metadata\n", "for idx, row in tqdm(entrez_gene_data.iterrows(), total=entrez_gene_data.shape[0]):\n", - " genes, lab, desc, syn = [], [], [], []\n", - " gene_id, sym, defn, gene_type = 'NCBIGene_' + str(row['GeneID']), row['Symbol'], row['description'], row['type_of_gene']\n", - " chrom, map_loc, s1, s2 = row['chromosome'], row['map_location'], row['Synonyms'], row['Other_designations']\n", - " dbxref = row['dbXrefs']\n", - " if gene_id != 'None':\n", + " if row['GeneID'] != 'None':\n", + " genes, lab, desc, syn = [], [], [], []\n", + " gene_id, sym, defn = row['GeneID'], row['Symbol'], row['description']\n", + " gene_type, dbxref = row['type_of_gene'], row['dbXrefs']\n", + " chrom, map_loc, s1, s2 = row['chromosome'], row['map_location'], row['Synonyms'], row['Other_designations']\n", " genes.append('http://www.ncbi.nlm.nih.gov/gene/' + str(gene_id))\n", " if sym != 'None' or sym != '': lab.append(sym)\n", " else: lab.append('Entrez_ID:' + gene_id)\n", @@ -4862,7 +4869,6 @@ " elif s1 != 'None': syn.append('|'.join(set([x for x in s1.split('|') if x != 'None' or x != ''])))\n", " elif s2 != 'None': syn.append('|'.join(set([x for x in s2.split('|') if x != 'None' or x != ''])))\n", " else: syn.append('None')\n", - " \n", " # update master dictionary\n", " master_metadata_dictionary['nodes'][gene_id] = {\n", " 'Label': ''.join(lab),\n", @@ -4878,14 +4884,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "##### RNA Metadata Dictionary <a class=\"anchor\" id=\"rna-metadata\"></a>\n", + "##### RNA Metadata Dictionary <a class=\"anchor\" id=\"rna-metadata\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Ensembl](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#ensembl)\n", "\n", - "The nested dictionary of rna metadata is created by looping over the cleaned human [Ensembl](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#ensembl) gene, RNA, and protein identifier data set (`ensembl_identifier_data_cleaned.txt`). The `keys` of the dictionary are `Ensembl transcript identifiers` and the `values` are dictionaries for each metadata type." + "The nested dictionary of rna metadata is created by looping over the cleaned human [Ensembl](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#ensembl) gene, RNA, and protein identifier data set (`ensembl_identifier_data_cleaned.txt`). The `keys` of the dictionary are `Ensembl transcript identifiers` and the `values` are dictionaries for each metadata type." ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -4902,6 +4910,9 @@ "# remove duplicates\n", "rna_gene_data.drop_duplicates(subset=['transcript_stable_id', 'transcript_name', 'ensembl_transcript_type'], keep='first', inplace=True)\n", "\n", + "# update prefixes\n", + "rna_gene_data['transcript_stable_id'] = 'ensembl_' + rna_gene_data['transcript_stable_id'].astype('str')\n", + "\n", "# replace NaN with 'None'\n", "rna_gene_data.fillna('None', inplace=True)" ] @@ -4910,20 +4921,12 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 24%|██▎ | 58528/248494 [00:11<00:36, 5141.47it/s]" - ] - } - ], + "outputs": [], "source": [ "# create metadata\n", "for idx, row in tqdm(rna_gene_data.iterrows(), total=rna_gene_data.shape[0]):\n", " rna, lab, desc, syn = [], [], [], []\n", - " rna_id = 'ensembl_' + row['transcript_stable_id']\n", + " rna_id = row['transcript_stable_id']\n", " ent_type, nme = row['ensembl_transcript_type'], row['transcript_name']\n", " rna.append('https://uswest.ensembl.org/Homo_sapiens/Transcript/Summary?t=' + rna_id)\n", " if nme != 'None': lab.append(nme)\n", @@ -4948,9 +4951,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "##### Variant Metadata Dictionary <a class=\"anchor\" id=\"variant-metadata\"></a> \n", + "##### Variant Metadata Dictionary <a class=\"anchor\" id=\"variant-metadata\"></a> \n", + "\n", + "**Data Source Wiki Page:** [ClinVar Variant](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#clinvar)\n", "\n", - "The nested dictionary of rna metadata is created by looping over the human [ClinVar Variant](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#clinvar) identifier data set (`variant_summary.txt`). The `keys` of the dictionary are `dbSNP identifiers` and the `values` are dictionaries for each metadata type." + "The nested dictionary of rna metadata is created by looping over the human [ClinVar Variant](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#clinvar) identifier data set ([`variant_summary.txt`](ftp://ftp.ncbi.nlm.nih.gov/pub/clinvar/tab_delimited/variant_summary.txt.gz)). The `keys` of the dictionary are `dbSNP identifiers` and the `values` are dictionaries for each metadata type." ] }, { @@ -4975,6 +4980,9 @@ "var_metadata = var_data[['VariationID', '#AlleleID', 'Type', 'Name', 'ClinicalSignificance', 'RS# (dbSNP)', 'Origin',\n", " 'ChromosomeAccession', 'Chromosome', 'Start', 'Stop', 'ReferenceAllele', 'OtherIDs',\n", " 'Assembly', 'AlternateAllele','Cytogenetic', 'ReviewStatus', 'LastEvaluated']] \n", + "# update prefixes\n", + "var_metadata['VariationID'] = 'clinvar_' + var_metadata['VariationID'].astype('str')\n", + "\n", "\n", "# replace NaN with 'None'\n", "var_metadata.replace('na', 'None', inplace=True)\n", @@ -4993,9 +5001,9 @@ "source": [ "# create metadata\n", "for idx, row in tqdm(var_metadata.iterrows(), total=var_metadata.shape[0]):\n", - " variant, label, desc, syn = [], [], [], []\n", - " var_id, lab, dbxref = 'clinvar_' + str(row['VariationID']), row['Name'], row['OtherIDs']\n", - " if var_id != 'None':\n", + " if row['VariationID'] != 'None':\n", + " variant, label, desc, syn = [], [], [], []\n", + " var_id, lab, dbxref = row['VariationID'], row['Name'], row['OtherIDs']\n", " variant.append('https://www.ncbi.nlm.nih.gov/snp/rs' + str(var_id))\n", " if lab != 'None': label.append(lab)\n", " else: label.append('dbSNP_ID:rs' + str(var_id))\n", @@ -5024,7 +5032,9 @@ "source": [ "##### Pathway Metadata Dictionary <a class=\"anchor\" id=\"pathway-metadata\"></a> \n", "\n", - "The nested dictionary of pathway metadata is created by looping over the human [Reactome Pathway Database](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#reactome-pathway-database) identifier data set (`ReactomePathways.txt`); Reactome-Gene Association data (`gene_association.reactome.gz`), and Reactome-ChEBI data (`ChEBI2Reactome_All_Levels.txt`). The `keys` of the dictionary are `Reactome identifiers` and the `values` are dictionaries for each metadata type." + "**Data Source Wiki Page:** [Reactome Pathway Database](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#reactome-pathway-database)\n", + "\n", + "The nested dictionary of pathway metadata is created by looping over the human [Reactome Pathway Database](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#reactome-pathway-database) identifier data set ([`ReactomePathways.txt`](https://reactome.org/download/current/ReactomePathways.txt)); Reactome-Gene Association data ([`gene_association.reactome.gz`](https://reactome.org/download/current/gene_association.reactome.gz)), and Reactome-ChEBI data ([`ChEBI2Reactome_All_Levels.txt`](https://reactome.org/download/current/ChEBI2Reactome_All_Levels.txt)). The `keys` of the dictionary are `Reactome identifiers` and the `values` are dictionaries for each metadata type." ] }, { @@ -5085,9 +5095,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "##### Relations Metadata Dictionary <a class=\"anchor\" id=\"relations-metadata\"></a> \n", + "##### Relations Metadata Dictionary <a class=\"anchor\" id=\"relations-metadata\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Relations Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#relations-ontology)\n", "\n", - "The nested dictionary of relation metadata is created by looping over the human [Relations Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v2-Data-Sources#relations-ontology) identifier data set (`ro_with_imports.owl`). The `keys` of the dictionary are `Relations Ontology identifiers` and the `values` are dictionaries for each metadata type." + "The nested dictionary of relation metadata is created by looping over the human [Relations Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#relations-ontology) identifier data set (`ro_with_imports.owl`). The `keys` of the dictionary are `Relations Ontology identifiers` and the `values` are dictionaries for each metadata type." ] }, { @@ -5167,7 +5179,9 @@ " - [ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_PRO_ONTOLOGY_MAP.txt) \n", " - [UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/UNIPROT_ACCESSION_PRO_ONTOLOGY_MAP.txt) \n", " - [STRING_PRO_ONTOLOGY_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/STRING_PRO_ONTOLOGY_MAP.txt)\n", - "- RNA: [ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt)\n", + "- RNA: \n", + " - [ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/ENTREZ_GENE_ENSEMBL_TRANSCRIPT_MAP.txt) \n", + " - [GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/GENE_SYMBOL_ENSEMBL_TRANSCRIPT_MAP.txt)\n", "- Diseases: [DISEASE_MONDO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/DISEASE_MONDO_MAP.txt) \n", "- Phenotypes: [PHENOTYPE_HPO_MAP.txt](https://storage.googleapis.com/pheknowlator/current_build/data/processed_data/PHENOTYPE_HPO_MAP.txt) " ] @@ -5256,9 +5270,9 @@ " 'probable N-acetyltransferase 8B'],\n", " 'PR': ['PR_Q9UHF3'],\n", " 'GeneSymbol': ['GeneSymbol_NAT8BP',\n", - " 'GeneSymbol_Hcml2',\n", - " 'GeneSymbol_CML2',\n", - " 'GeneSymbol_NAT8B'],\n", + " 'GeneSymbol_Hcml2',\n", + " 'GeneSymbol_CML2',\n", + " 'GeneSymbol_NAT8B'],\n", " 'ensembl gene': ['ensembl_ENSG00000204872'],\n", " 'ensembl protein': ['ensembl_ENSP00000485054'],\n", " 'map_location': ['2p13.1'],\n", @@ -5271,6 +5285,22 @@ "```" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# load data -- only reload if the dictionary has not been populated\n", + "if not 'reformatted_mapped_identifiers' in locals():\n", + " filepath = processed_data_location + 'Merged_gene_rna_protein_identifiers.pkl'\n", + " max_bytes = 2**31 - 1; input_size = os.path.getsize(filepath); bytes_in = bytearray(0)\n", + " with open(filepath, 'rb') as f_in:\n", + " for _ in range(0, input_size, max_bytes):\n", + " bytes_in += f_in.read(max_bytes)\n", + " reformatted_mapped_identifiers = pickle.loads(bytes_in)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -5319,7 +5349,10 @@ "source": [ "***\n", "\n", - "#### `CTD_chem_gene_ixns.tsv` <a class=\"anchor\" id=\"chemical-gene\"></a>\n", + "#### `CTD_chem_gene_ixns.tsv` <a class=\"anchor\" id=\"chemical-gene\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Comparative Toxicogenomics Database](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#comparative-toxicogenomics-database)\n", + "\n", "\n", "**Edges:** \n", "- `chemical-gene` \n", @@ -5407,13 +5440,12 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'] = {'chemical-gene': {}, 'chemical-rna': {}, 'chemical-protein': {}}\n", + "# master_metadata_dictionary['edges'] = {'chemical-gene': {}, 'chemical-rna': {}, 'chemical-protein': {}}\n", "\n", "# create dictionary\n", "for idx, row in tqdm(ctd_gene_inx.iterrows(), total=ctd_gene_inx.shape[0]):\n", " chebi = row['CHEBI_ID'].rstrip(); gene_form = None\n", " chemical_name = row['# ChemicalName']; chemical_id = row['ChemicalID'].rstrip(); casrn = row['CasRN']\n", - " \n", " evidence = [{'CTD_Interaction': row['Interaction'],\n", " 'CTD_InteractionActions': row['InteractionActions'],\n", " 'CTD_PubMedIDs': row['PubMedIDs']}]\n", @@ -5461,14 +5493,19 @@ " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", "\n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'CTD_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['CTD_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['CTD_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'CTD_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'CTD_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'CTD_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['CTD_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['CTD_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'CTD_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'CTD_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'CTD_Evidence': evidence, 'Type': edge_type}}})\n", + " \n", + "# delete unneeded data\n", + "del ctd_gene_inx" ] }, { @@ -5477,7 +5514,9 @@ "source": [ "***\n", "\n", - "#### `CTD_chem_go_enriched.tsv` <a class=\"anchor\" id=\"chemical-go\"></a>\n", + "#### `CTD_chem_go_enriched.tsv` <a class=\"anchor\" id=\"chemical-go\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Comparative Toxicogenomics Database](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#comparative-toxicogenomics-database)\n", "\n", "**Edges:** \n", "- `chemical-gobp` \n", @@ -5562,7 +5601,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'chemical-gobp': {}, 'chemical-gocc': {}, 'chemical-gomf': {}})\n", + "# master_metadata_dictionary['edges'].update({'chemical-gobp': {}, 'chemical-gocc': {}, 'chemical-gomf': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(ctd_chem_go.iterrows(), total=ctd_chem_go.shape[0]):\n", @@ -5613,14 +5652,19 @@ " 'CTD_GOTermName': {go_name}}}})\n", " \n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'CTD_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['CTD_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['CTD_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'CTD_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'CTD_Evidence': evidence}} \n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'CTD_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['CTD_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['CTD_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'CTD_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'CTD_Evidence': evidence, 'Type': edge_type}}) \n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'CTD_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del ctd_chem_go" ] }, { @@ -5629,7 +5673,9 @@ "source": [ "***\n", "\n", - "#### `CTD_chemicals_diseases.tsv` <a class=\"anchor\" id=\"chemical-disease\"></a>\n", + "#### `CTD_chemicals_diseases.tsv` <a class=\"anchor\" id=\"chemical-disease\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Comparative Toxicogenomics Database](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#comparative-toxicogenomics-database)\n", "\n", "**Edges:** \n", "- `chemical-disease` \n", @@ -5714,7 +5760,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'chemical-disease': {}, 'chemical-phenotype': {}})\n", + "# master_metadata_dictionary['edges'].update({'chemical-disease': {}, 'chemical-phenotype': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(ctd_chem_dis.iterrows(), total=ctd_chem_dis.shape[0]):\n", @@ -5767,15 +5813,19 @@ " 'CTD_OmimIDs': {omim}}}})\n", " \n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'CTD_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['CTD_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['CTD_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'CTD_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'CTD_Evidence': evidence}}\n", - " " + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'CTD_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['CTD_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['CTD_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'CTD_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'CTD_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'CTD_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del ctd_chem_dis" ] }, { @@ -5784,7 +5834,9 @@ "source": [ "***\n", "\n", - "#### `ChEBI2Reactome_All_Levels.txt` <a class=\"anchor\" id=\"gene-pathway\"></a>\n", + "#### `ChEBI2Reactome_All_Levels.txt` <a class=\"anchor\" id=\"gene-pathway\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Reactome Pathway Database](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#reactome-pathway-database) \n", "\n", "\n", "**Edges:** \n", @@ -5836,7 +5888,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'chemical-pathway': {}})\n", + "# master_metadata_dictionary['edges'].update({'chemical-pathway': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(rtm_chem_path.iterrows(), total=rtm_chem_path.shape[0]):\n", @@ -5856,14 +5908,19 @@ " url: {'Reactome_PathwayName': {path_name}}}})\n", " \n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'Reactome_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['Reactome_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Reactome_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Reactome_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Reactome_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'Reactome_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['Reactome_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['Reactome_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'Reactome_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'Reactome_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'Reactome_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del rtm_chem_path" ] }, { @@ -5872,7 +5929,10 @@ "source": [ "***\n", "\n", - "#### `goa_human.gaf` <a class=\"anchor\" id=\"goa\"></a>\n", + "#### `goa_human.gaf` <a class=\"anchor\" id=\"goa\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Gene Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#gene-ontology) \n", + "\n", "\n", "**Edges:** \n", "- `protein-gobp` \n", @@ -5980,7 +6040,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'protein-gobp': {}, 'protein-gocc': {}, 'protein-gomf': {}})\n", + "# master_metadata_dictionary['edges'].update({'protein-gobp': {}, 'protein-gocc': {}, 'protein-gomf': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(goa_gene.iterrows(), total=goa_gene.shape[0]):\n", @@ -6025,15 +6085,19 @@ " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", "\n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'GOA_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['GOA_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['GOA_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'GOA_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'GOA_Evidence': evidence}}\n", - " " + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'GOA_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['GOA_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['GOA_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'GOA_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'GOA_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'GOA_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del goa_gene" ] }, { @@ -6042,7 +6106,9 @@ "source": [ "***\n", "\n", - "#### `COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt` <a class=\"anchor\" id=\"gene-gene\"></a>\n", + "#### `COMBINED.DEFAULT_NETWORKS.BP_COMBINING.txt` <a class=\"anchor\" id=\"gene-gene\"></a> \n", + "\n", + "**Data Source Wiki Page:** [GeneMANIA](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#genemania) \n", "\n", "**Edges:** \n", "- `gene-gene` \n", @@ -6108,7 +6174,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'gene-gene': {}})\n", + "# master_metadata_dictionary['edges'].update({'gene-gene': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(gm_gene_gene.iterrows(), total=gm_gene_gene.shape[0]):\n", @@ -6130,11 +6196,16 @@ " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}}) \n", " \n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'GeneMania_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['GeneMania_Evidence'] = weight\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'GeneMania_Evidence': weight}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'GeneMania_Evidence': weight}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'GeneMania_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " master_metadata_dictionary['edges'][edge_key][url]['GeneMania_Evidence'] = weight\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'GeneMania_Evidence': weight})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'GeneMania_Evidence': weight, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'GeneMania_Evidence': weight, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del gm_gene_gene" ] }, { @@ -6143,7 +6214,9 @@ "source": [ "***\n", "\n", - "#### `phenotype.hpoa` <a class=\"anchor\" id=\"phenotype-disease\"></a>\n", + "#### `phenotype.hpoa` <a class=\"anchor\" id=\"phenotype-disease\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Human Phenotype Ontology](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#human-phenotype-ontology) \n", "\n", "**Edges:** \n", "- `disease-phenotype` \n", @@ -6223,7 +6296,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'disease-phenotype': {}})\n", + "# master_metadata_dictionary['edges'].update({'disease-phenotype': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(hpo_dis_phe.iterrows(), total=hpo_dis_phe.shape[0]):\n", @@ -6245,14 +6318,19 @@ " else: master_metadata_dictionary['nodes'].update({node_key: {url: {'HPO_DiseaseName': disease_names[node_key]}}})\n", " \n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'HPO_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['HPO_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['HPO_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'HPO_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'HPO_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'HPO_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['HPO_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['HPO_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'HPO_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'HPO_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'HPO_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del hpo_dis_phe" ] }, { @@ -6261,7 +6339,9 @@ "source": [ "***\n", "\n", - "#### `gene_association.reactome` <a class=\"anchor\" id=\"reactome-go\"></a>\n", + "#### `gene_association.reactome` <a class=\"anchor\" id=\"reactome-go\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Reactome Pathway Database](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#reactome-pathway-database) \n", "\n", "**Edges:** \n", "- `gobp-pathway` \n", @@ -6347,7 +6427,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'gobp-pathway': {}, 'pathway-gocc': {}, 'pathway-gomf': {}})\n", + "# master_metadata_dictionary['edges'].update({'gobp-pathway': {}, 'pathway-gocc': {}, 'pathway-gomf': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(rce_go_ptw.iterrows(), total=rce_go_ptw.shape[0]):\n", @@ -6375,14 +6455,19 @@ " else: master_metadata_dictionary['nodes'].update({node_key: {url: {'Reactome_Aspect': {aspect}}}})\n", " \n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'Reactome_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['Reactome_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Reactome_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Reactome_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Reactome_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'Reactome_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['Reactome_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['Reactome_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'Reactome_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'Reactome_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'Reactome_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del rce_go_ptw" ] }, { @@ -6391,7 +6476,9 @@ "source": [ "***\n", "\n", - "#### `UniProt2Reactome_All_Levels.txt` <a class=\"anchor\" id=\"uniprot-react\"></a>\n", + "#### `UniProt2Reactome_All_Levels.txt` <a class=\"anchor\" id=\"uniprot-react\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Reactome Pathway Database](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#reactome-pathway-database) \n", "\n", "**Edges:** \n", "- `protein-pathway` \n", @@ -6487,7 +6574,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'protein-pathway': {}})\n", + "# master_metadata_dictionary['edges'].update({'protein-pathway': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(rce_prot_pth.iterrows(), total=rce_prot_pth.shape[0]):\n", @@ -6517,14 +6604,19 @@ " else: master_metadata_dictionary['nodes'].update({pr: {'genomic_data': 'None'}})\n", "\n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'Reactome_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['Reactome_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Reactome_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Reactome_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Reactome_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'Reactome_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['Reactome_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['Reactome_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'Reactome_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'Reactome_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'Reactome_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del rce_prot_pth" ] }, { @@ -6533,7 +6625,9 @@ "source": [ "***\n", "\n", - "#### `CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt` <a class=\"anchor\" id=\"variant-disease\"></a>\n", + "#### `CLINVAR_VARIANT_DISEASE_PHENOTYPE_EDGES.txt` <a class=\"anchor\" id=\"variant-disease\"></a> \n", + "\n", + "**Data Source Wiki Page:** [ClinVar](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#clinvar) \n", "\n", "**Edges:** \n", "- `variant-disease` \n", @@ -6624,7 +6718,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'variant-disease': {}, 'variant-phenotype': {}})\n", + "# master_metadata_dictionary['edges'].update({'variant-disease': {}, 'variant-phenotype': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(clv_var_dis.iterrows(), total=clv_var_dis.shape[0]):\n", @@ -6683,14 +6777,19 @@ " 'ClinVar_Assembly': {assembly}}}})\n", "\n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'ClinVar_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['ClinVar_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['ClinVar_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'ClinVar_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'ClinVar_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'ClinVar_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['ClinVar_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['ClinVar_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'ClinVar_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'ClinVar_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'ClinVar_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del clv_var_dis" ] }, { @@ -6699,7 +6798,9 @@ "source": [ "***\n", "\n", - "#### `CLINVAR_VARIANT_GENE_EDGES.txt` <a class=\"anchor\" id=\"variant-gene\"></a>\n", + "#### `CLINVAR_VARIANT_GENE_EDGES.txt` <a class=\"anchor\" id=\"variant-gene\"></a> \n", + "\n", + "**Data Source Wiki Page:** [ClinVar](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#clinvar) \n", "\n", "**Edges:** \n", "- `variant-gene` \n", @@ -6775,7 +6876,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'variant-gene': {}})\n", + "# master_metadata_dictionary['edges'].update({'variant-gene': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(clv_var_gene.iterrows(), total=clv_var_gene.shape[0]):\n", @@ -6796,13 +6897,6 @@ " 'ClinVar_NumberSubmitters': row['NumberSubmitters'],\n", " 'ClinVar_Citation': row['Citation']}] \n", " edge_key = '{}-{}'.format(node_key, gene); edge_type = 'variant-gene'\n", - " \n", - " # add disease/phenotype metadata\n", - " if idx in master_metadata_dictionary['nodes'].keys():\n", - " if url in master_metadata_dictionary['nodes'][idx].keys():\n", - " master_metadata_dictionary['nodes'][idx][url]['ClinVar_Phenotype'] |= {pheno}\n", - " else: master_metadata_dictionary['nodes'][idx].update({url: {'ClinVar_Phenotype': {pheno}}})\n", - " else: master_metadata_dictionary['nodes'].update({idx: {url: {'ClinVar_Phenotype': {pheno}}}})\n", "\n", " # add genomic information\n", " if gene in genomic_metadata.keys(): genomic_info_dict = genomic_metadata[gene]\n", @@ -6850,14 +6944,19 @@ " 'ClinVar_Category': {category}}}})\n", "\n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'ClinVar_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['ClinVar_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['ClinVar_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'ClinVar_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'ClinVar_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'ClinVar_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['ClinVar_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['ClinVar_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'ClinVar_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'ClinVar_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'ClinVar_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del clv_var_gene " ] }, { @@ -6866,7 +6965,11 @@ "source": [ "***\n", "\n", - "#### `HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt` <a class=\"anchor\" id=\"hpa\"></a>\n", + "#### `HPA_GTEX_RNA_GENE_PROTEIN_EDGES.txt` <a class=\"anchor\" id=\"hpa\"></a> \n", + "\n", + "**Data Source Wiki Page:** \n", + "- [Genotype-Tissue Expression Project](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#genotype-tissue-expression-project) \n", + "- [Human Protein Atlas](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#human-protein-atlas) \n", "\n", "**Edges:** \n", "- `protein-anatomy` \n", @@ -6949,7 +7052,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'protein-anatomy': {}, 'protein-cell': {}, 'rna-anatomy': {}, 'rna-cell': {}})\n", + "# master_metadata_dictionary['edges'].update({'protein-anatomy': {}, 'protein-cell': {}, 'rna-anatomy': {}, 'rna-cell': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(hpa_gen_ant.iterrows(), total=hpa_gen_ant.shape[0]):\n", @@ -6995,15 +7098,19 @@ " else: master_metadata_dictionary['nodes'].update({node_key2: {'genomic_data': 'None'}})\n", "\n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'HPA_GTEx_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['HPA_GTEx_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['HPA_GTEx_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'HPA_GTEx_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'HPA_GTEx_Evidence': evidence}}\n", - "\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'HPA_GTEx_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['HPA_GTEx_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['HPA_GTEx_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'HPA_GTEx_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'HPA_GTEx_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'HPA_GTEx__Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del hpa_gen_ant " ] }, { @@ -7012,7 +7119,9 @@ "source": [ "***\n", "\n", - "#### `UNIPROT_PROTEIN_CATALYST.txt` <a class=\"anchor\" id=\"uniprot-catalyst\"></a>\n", + "#### `UNIPROT_PROTEIN_CATALYST.txt` <a class=\"anchor\" id=\"uniprot-catalyst\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Universal Protein Resource Knowledgebase](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#universal-protein-resource-knowledgebase) \n", "\n", "**Edges:** \n", "- `protein-catalyst` \n", @@ -7059,7 +7168,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'protein-catalyst': {}})\n", + "# master_metadata_dictionary['edges'].update({'protein-catalyst': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(upt_prot_cat.iterrows(), total=upt_prot_cat.shape[0]):\n", @@ -7085,14 +7194,19 @@ " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", "\n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'Uniprot_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['Uniprot_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Uniprot_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Uniprot_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Uniprot_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'Uniprot_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['Uniprot_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['Uniprot_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'Uniprot_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'Uniprot_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'Uniprot_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del upt_prot_cat" ] }, { @@ -7101,7 +7215,9 @@ "source": [ "***\n", "\n", - "#### `UNIPROT_PROTEIN_COFACTOR.txt` <a class=\"anchor\" id=\"uniprot-cofactor\"></a>\n", + "#### `UNIPROT_PROTEIN_COFACTOR.txt` <a class=\"anchor\" id=\"uniprot-cofactor\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Universal Protein Resource Knowledgebase](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#universal-protein-resource-knowledgebase) \n", "\n", "**Edges:** \n", "- `protein-cofactor` \n", @@ -7148,7 +7264,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'protein-cofactor': {}})\n", + "# master_metadata_dictionary['edges'].update({'protein-cofactor': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(upt_prot_cof.iterrows(), total=upt_prot_cof.shape[0]):\n", @@ -7174,14 +7290,19 @@ " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", "\n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'Uniprot_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['Uniprot_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['Uniprot_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'Uniprot_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'Uniprot_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'Uniprot_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['Uniprot_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['Uniprot_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'Uniprot_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'Uniprot_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'Uniprot_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del upt_prot_cof" ] }, { @@ -7190,7 +7311,9 @@ "source": [ "***\n", "\n", - "#### `9606.protein.links.v11.0.txt.gz` <a class=\"anchor\" id=\"protein-protein\"></a>\n", + "#### `9606.protein.links.v11.0.txt.gz` <a class=\"anchor\" id=\"protein-protein\"></a> \n", + "\n", + "**Data Source Wiki Page:** [Search Tool for Recurring Instances of Neighbouring Genes Database](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#search-tool-for-recurring-instances-of-neighbouring-genes-database) \n", "\n", "**Edges:** \n", "- `protein-protein` \n", @@ -7257,7 +7380,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'protein-protein': {}})\n", + "# master_metadata_dictionary['edges'].update({'protein-protein': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(stg_prot_prot.iterrows(), total=stg_prot_prot.shape[0]):\n", @@ -7279,12 +7402,16 @@ " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}}) \n", " \n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'String_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['String_Evidence'] = score\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'String_Evidence': score}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'String_Evidence': score}}\n", - "\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'String_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " master_metadata_dictionary['edges'][edge_key][url]['String_Evidence'] = score\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'String_Evidence': score})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'String_Evidence': score, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'String_Evidence': score, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del stg_prot_prot" ] }, { @@ -7293,7 +7420,9 @@ "source": [ "***\n", "\n", - "#### `curated_gene_disease_associations.tsv` <a class=\"anchor\" id=\"gene-phen\"></a>\n", + "#### `curated_gene_disease_associations.tsv` <a class=\"anchor\" id=\"gene-phen\"></a> \n", + "\n", + "**Data Source Wiki Page:** [DisGeNET](https://github.com/callahantiff/PheKnowLator/wiki/v4-Data-Sources#disgenet)\n", "\n", "**Edges:** \n", "- `gene-disease` \n", @@ -7319,7 +7448,7 @@ " - `DSI`: The Disease Similarity Index ranges from from 0.25 to 1. It is calculated as: DSI = log2(# diseases assoc with gene/total # of diseases in DisGeNET) / log2(1/total # of diseases in DisGeNET) \n", " - `DPI`: The Disease Pleiotropy Index ranges from 0 to 1. it is calculated as: DPI = (# of MeSH disease classes of disease assoc with gene/total # of MeSH disease classes)*100. \n", " - `score`: The score range from 0 to 1, and take into account the number and type of sources (level of curation, model organisms), and the number of publications supporting the association. \n", - " - `EI`: The Evidence Index(EL) is a metric developed by ClinGen that measures the strength of evidence of a gene-disease relationship that correlates to a qualitative classification: \"Definitive\", \"Strong\", \"Moderate\", \"Limited\", \"Disputed\" (Strande et al., 2017). EI = 1 indicates that all the publications support the GDA or the VDA, while EI < 1 indicates that there are publications that assert that there is no association between the gene/variants and the disease. If the gene/variant has no EI value, it indicates that the index has not been computed for this association. It is calculated as: EI = (# positive pubs/total # of pubs). \n", + " - `EI`: The Evidence Index (EL) is a metric developed by ClinGen that measures the strength of evidence of a gene-disease relationship that correlates to a qualitative classification: \"Definitive\", \"Strong\", \"Moderate\", \"Limited\", \"Disputed\" ([PMID:28552198](https://www.ncbi.nlm.nih.gov/pubmed/28552198)). EI=1 indicates that all the publications support the GDA or the VDA, while EI<1 indicates that there are publications that assert that there is no association between the gene/variants and the disease. If the gene/variant has no EI value, it indicates that the index has not been computed for this association. It is calculated as: EI = (# positive pubs/total # of pubs). \n", " - `YearInitial`: First time that the association was reported. \n", " - `YearFinal`: Last time that the association was reported. \n", " - `NofPmids`: Count of associated Pubmed IDs. \n", @@ -7383,7 +7512,7 @@ "metadata": {}, "outputs": [], "source": [ - "master_metadata_dictionary['edges'].update({'gene-disease': {}, 'gene-phenotype': {}})\n", + "# master_metadata_dictionary['edges'].update({'gene-disease': {}, 'gene-phenotype': {}})\n", "\n", "# create dictionary\n", "for idx, row in tqdm(dgt_dis_gene.iterrows(), total=dgt_dis_gene.shape[0]):\n", @@ -7434,14 +7563,19 @@ " else: master_metadata_dictionary['nodes'].update({node_key: {'genomic_data': 'None'}})\n", "\n", " # add relation data to dictionary\n", - " if edge_key in master_metadata_dictionary['edges'][edge_type].keys():\n", - " if 'DisGeNET_Evidence' in master_metadata_dictionary['edges'][edge_type][edge_key][url].keys():\n", - " inital_ev = master_metadata_dictionary['edges'][edge_type][edge_key][url]\n", - " inital_ev = inital_ev['DisGeNET_Evidence'] + evidence\n", - " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", - " master_metadata_dictionary['edges'][edge_type][edge_key][url]['DisGeNET_Evidence'] = ev\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key][url] = {'DisGeNET_Evidence': evidence}\n", - " else: master_metadata_dictionary['edges'][edge_type][edge_key] = {url: {'DisGeNET_Evidence': evidence}}\n" + " if edge_key in master_metadata_dictionary['edges'].keys():\n", + " if url in master_metadata_dictionary['edges'][edge_key].keys():\n", + " if 'DisGeNET_Evidence' in master_metadata_dictionary['edges'][edge_key][url].keys():\n", + " inital_ev = master_metadata_dictionary['edges'][edge_key][url]\n", + " inital_ev = inital_ev['DisGeNET_Evidence'] + evidence\n", + " ev = [json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in inital_ev)]\n", + " master_metadata_dictionary['edges'][edge_key][url]['DisGeNET_Evidence'] = ev\n", + " else: master_metadata_dictionary['edges'][edge_key][url].update({'DisGeNET_Evidence': evidence})\n", + " else: master_metadata_dictionary['edges'][edge_key].update({url: {'DisGeNET_Evidence': evidence, 'Type': edge_type}})\n", + " else: master_metadata_dictionary['edges'].update({edge_key: {url: {'DisGeNET_Evidence': evidence, 'Type': edge_type}}})\n", + "\n", + "# delete unneeded data\n", + "del dgt_dis_gene" ] }, { @@ -7456,6 +7590,138 @@ "Write the metadata dictionary to a file named `entity_metadata_dict.pkl` and located in the `resources/metadata/` directory." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Node Data*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # create list of dictionaries\n", + "# print('Creating Node List ...')\n", + "# node_list = [{k: v} for k, v in tqdm(master_metadata_dictionary['nodes'].items())]\n", + "# master_metadata_dictionary['nodes'] = {} " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "node_temp = {}\n", + "for k, v in tqdm(master_metadata_dictionary['nodes'].items()):\n", + " file_loc = metadata_location + 'temp/nodes/' + k + '.json'\n", + " # write data to temp directory\n", + " dump_jsonl([v[k]], file_loc)\n", + " # add dictionary entry with file path\n", + " node_temp[k] = file_loc\n", + " # delete entry\n", + " del master_metadata_dictionary['nodes'][k]\n", + "\n", + "# update nodes entry\n", + "master_metadata_dictionary['nodes'] = node_temp\n", + " \n", + "# remove unneeded data\n", + "del node_list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Relation Data*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print('\\nCreating Relations List ...')\n", + "# relation_list = [{k: v} for k, v in tqdm(master_metadata_dictionary['relations'].items())]\n", + "# master_metadata_dictionary['relations'] = {} " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "relations_temp = {}\n", + "for k, v in tqdm(master_metadata_dictionary['relations'].items()):\n", + " file_loc = metadata_location + 'temp/relations/' + k + '.json'\n", + " # write data to temp directory\n", + " dump_jsonl([v[k]], file_loc)\n", + " # add dictionary entry with file path\n", + " relations_temp[k] = file_loc\n", + " # delete entry\n", + " del master_metadata_dictionary['relations'][k]\n", + "\n", + "# update nodes entry\n", + "master_metadata_dictionary['relations'] = relations_temp\n", + " \n", + "# remove unneeded data\n", + "del relation_list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Edge Data*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # print('\\nCreating Edge List ...')\n", + "# # edge_list = [{k: v} for k, v in tqdm(master_metadata_dictionary['edges'].items())]\n", + "# master_metadata_dictionary['edges'] = {} " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "edges_temp = {}\n", + "for k, v in tqdm(master_metadata_dictionary['edges'].items()):\n", + " file_loc = metadata_location + 'temp/edges/' + k + '.json'\n", + " # write data to temp directory\n", + " dump_jsonl([v[k]], file_loc)\n", + " # add dictionary entry with file path\n", + " edges_temp[k] = file_loc\n", + " # delete entry\n", + " del master_metadata_dictionary['edges'][k]\n", + " \n", + "\n", + "# update nodes entry\n", + "master_metadata_dictionary['edges'] = edges_temp\n", + " \n", + "# remove unneeded data\n", + "del edge_list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Save Updated Metadata Dictionary to Metadata Location*" + ] + }, { "cell_type": "code", "execution_count": null, @@ -7475,20 +7741,6 @@ " f_out.write(bytes_out[idx:idx+max_bytes])" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(edge_type, edge_key, node_key)\n", - "break\n", - " \n", - "# master_metadata_dictionary['edges']['chemical-gobp']['CHEBI_44975-GO_0046031']\n", - "# master_metadata_dictionary['nodes']['GO_0046031']\n", - "master_metadata_dictionary['nodes']['CHEBI_44975']" - ] - }, { "cell_type": "markdown", "metadata": {}, From cfb11f0f5d49dff781b2e2e185e9d9a9073cc3e8 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:17:48 -0400 Subject: [PATCH 092/112] adding analytic search examples --- notebooks/Entity_Search_Examples.ipynb | 1257 ++++++++++++++++++++++++ 1 file changed, 1257 insertions(+) create mode 100644 notebooks/Entity_Search_Examples.ipynb diff --git a/notebooks/Entity_Search_Examples.ipynb b/notebooks/Entity_Search_Examples.ipynb new file mode 100644 index 00000000..c68ab6c6 --- /dev/null +++ b/notebooks/Entity_Search_Examples.ipynb @@ -0,0 +1,1257 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "***\n", + "***\n", + "\n", + "<img width='700' src=\"https://user-images.githubusercontent.com/8030363/108961534-b9a66980-7634-11eb-96e2-cc46589dcb8c.png\" style=\"vertical-align:middle\">\n", + "\n", + "## Knowledge Graph Entity Search Examples\n", + "\n", + "***\n", + "\n", + "**Author:** [TJCallahan](http://tiffanycallahan.com/) \n", + "**GitHub Repository:** [PheKnowLator](https://github.com/callahantiff/PheKnowLator/wiki) \n", + "**Wiki Page:** [OWL-NETS-2.0](https://github.com/callahantiff/PheKnowLator/wiki/OWL-NETS-2.0) \n", + "**Release:** **[v3.0.0](https://github.com/callahantiff/PheKnowLator/wiki/v3.0.0)** \n", + " \n", + "<br> \n", + "\n", + "### Purpose \n", + "The goal of this notebook is to explore different ways to examine relationships between entities in a PheKnowLator knowledge graph.\n", + "\n", + "#### PheKnowLator Knowledge Graph Build \n", + "This notebook was built using a `v3.0.2` OWL-NETS-abstracted subclass-based build with inverse relations, which is publicly available and can be downloaded using the following links: \n", + "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle) \n", + "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "*** \n", + "## Set-Up Environment \n", + "*** \n", + "\n", + "### Dependencies: [pkt_kg](https://pypi.org/project/pkt-kg/), [networkx](https://pypi.org/project/networkx/), [rdflib](https://pypi.org/project/rdflib/)\n", + "\n", + "To prepare for the tutorial we need to make sure that the all needed libraries are downloaded and imported. If you don't already have `pkt_kg`, `rdflib`, and `networkx` installed, you can extend the code chunk below to include any libraries that you need to download. In addition to downloading needed libraries, you will also need to download the specific version of each knowledge graph that you want to analyze. Each data source is briefly described in the next section. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # uncomment and run to install any required modules from notebooks/requirements.txt\n", + "# import sys\n", + "# !{sys.executable} -m pip install -r ../../notebooks/requirements.txt" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# # if running a local version of pkt_kg, uncomment the code below\n", + "# import sys\n", + "# sys.path.append('../')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# import needed libraries\n", + "import json\n", + "import networkx as nx\n", + "import os\n", + "import pandas as pd\n", + "import pickle\n", + "import random\n", + "import re\n", + "\n", + "from pkt_kg.utils import *\n", + "from rdflib import Graph, Namespace, URIRef, BNode, Literal\n", + "from rdflib.namespace import RDFS\n", + "from tqdm.notebook import tqdm\n", + "from typing import Callable, Dict, List, Optional, Union\n", + "\n", + "# create namespace for OBO ontologies\n", + "obo = Namespace('http://purl.obolibrary.org/obo/')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Knowledge Graph \n", + "The initial exploration will be performed using a `v3.0.2` OWL-NETS-abstracted subclass-based build with inverse relations, which is publicly available and can be downloaded using the following links: \n", + "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle) \n", + "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt) \n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# notebook will create a temporary directory\n", + "# write_location = '../releases/Columbia_Collaboration/tara_anand/data/'\n", + "write_location = '../temp_directory/'\n", + "if not os.path.exists(write_location):\n", + " os.mkdir(write_location)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# download data to the data directory\n", + "data_urls = [\n", + " 'https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle',\n", + " 'https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt',\n", + " 'https://www.dropbox.com/s/ev0ea6v6fu70fbl/entity_metadata_dict.pkl?dl=1'\n", + "]\n", + "\n", + "for url in data_urls:\n", + " file_name = url.split('/')[-1] if 'entity_metadata_dict.pkl' not in url else re.sub(r'\\?.*', '', url.split('/')[-1])\n", + " if not os.path.exists(write_location + file_name):\n", + " data_downloader(url, write_location, file_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Helper Functions \n", + "Create helper functions that are needed to process node data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [ + 28, + 54, + 80, + 105 + ] + }, + "outputs": [], + "source": [ + "def nx_ancestor_search(kg: nx.multidigraph.MultiDiGraph, nodes: List, prefix: str, anc_list: Optional[List]=None) -> Union[Callable, List]:\n", + " \"\"\"Returns all ancestors nodes reachable through a direct edge. The returned list is ordered by senority.\n", + " \n", + " Args:\n", + " kg: A networkx MultiDiGraph object.\n", + " nodes: A list of RDFLib URIRef objects or None.\n", + " prefix: A string containing an ontology prefix (e..g., MONDO).\n", + " anc_list: A list that is empty or that contains RDFLib URIRef objects.\n", + " \n", + " Returns:\n", + " anc_list: A list of period-delimited strings, where each string represents a path \n", + " \"\"\"\n", + " \n", + " ancestor_list = [] if anc_list is None else anc_list\n", + " \n", + " if len(nodes) == 0: return ancestor_list\n", + " else:\n", + " node = nodes.pop()\n", + " node_list = list(kg.neighbors(node))\n", + " neighborhood = [a for b in [[[i, n] for j in [kg.get_edge_data(*(node, n)).keys()]\n", + " for i in j] for n in node_list] for a in b]\n", + " ancestors = [x[1] for x in neighborhood if (prefix in str(x[1]) and x[0] == RDFS.subClassOf)]\n", + " if len(ancestors) > 0:\n", + " ancestor_list += [[str(x) for x in ancestors]]\n", + " nodes += ancestors\n", + " return nx_ancestor_search(kg, nodes, prefix, ancestor_list)\n", + " \n", + " \n", + "def processes_ancestor_path_list(path_list: List, node_metadata: Dict) -> Dict:\n", + " \"\"\"Processes a nested list of ancestor paths into a single unique list.\n", + " \n", + " Args:\n", + " path_list: A nested list of ontology URLs, where each list represents a set of ancestors.\n", + " node_metadata: A dictionary \n", + " \n", + " Returns:\n", + " ancestors: A nested list where each inner list contains ontology identifier strings\n", + " \"\"\"\n", + " \n", + " anc_dict = dict()\n", + " \n", + " for path in path_list:\n", + " for x in path:\n", + " idx = max([i for i, j in enumerate(path_list) if x in j])\n", + " if str(idx) in anc_dict.keys(): anc_dict[str(idx)] |= {x}\n", + " else: anc_dict[str(idx)] = {x}\n", + "\n", + " # reorder and format keys\n", + " ancestors = [['{} ({})'.format(node_data_dict[str(x)]['label'], x) for x in anc_dict[str(k)]]\n", + " for k in sorted([int(x) for x in anc_dict.keys()])]\n", + " \n", + " return ancestors\n", + "\n", + "\n", + "def formats_node_information(neighborhood: List, metadata_dict: Dict, verbose: bool=False) -> None:\n", + " \"\"\"Processes neighborhood results.\n", + " \n", + " Args:\n", + " neighborhood: A nested list of strings, where each string contains a node identifier.\n", + " metadata_dict: node_metadata: A nested dictionary containing node attributes.\n", + " verbose: A bool indicating whether or not node and edge metadata should be printed.\n", + " \n", + " \n", + " Returns:\n", + " None\n", + " \"\"\"\n", + " \n", + " for e, o in neighborhood:\n", + " spe = '\\n' if neighborhood.index([e, o]) == 0 else '\\n\\n'\n", + " s, s_label = str(node[0]).split('/')[-1], metadata_dict[str(node[0])]['label']\n", + " e_label = metadata_dict[str(e)]['label']\n", + " o, o_label, o_def = str(o).split('/')[-1], metadata_dict[str(o)]['label'], metadata_dict[str(o)]['description']\n", + " if verbose:\n", + " if o_def != 'None': print(spe + '>>> {} ({}) - {} - {} ({})\\n{} Definition: {}'.format(s_label, s, e_label, o_label, o, o, o_def))\n", + " else: print(spe + '>>> {} ({}) - {} - {} ({})'.format(s_label, s, e_label, o_label, o))\n", + " else: print('>>> {} ({}) - {} - {} ({})'.format(s_label, s, e_label, o_label, o))\n", + " \n", + " return None\n", + " \n", + "\n", + "def metadata_formatter(s: str, o: str, metadata_dict: Dict) -> None:\n", + " \"\"\"Function looks up edge-level metadata and prints it.\n", + " \n", + " Args:\n", + " s: A string containing the identifier for the subject node of a predicate or triple.\n", + " o: A string containing the identifier for the object node of a predicate or triple.\n", + " metadata_dict: A nested dictionary containing node and edge-level metadata.\n", + " \n", + " Returns:\n", + " None.\n", + " \"\"\"\n", + " \n", + " s = s + '-reactome_' if 'R-HSA' in s else s\n", + " o = o + '-reactome_' if 'R-HSA' in o else o\n", + " \n", + " if s + '-' + o in metadata_dict['edges'].keys():\n", + " print('\\nEdge Evidence'); print(json.dumps(metadata_dict['edges'][s + '-' + o], indent=4))\n", + " elif o + '-' + s in metadata_dict['edges'].keys():\n", + " print('\\nEdge Evidence'); print(json.dumps(metadata_dict['edges'][o + '-' + s], indent=4))\n", + " else:\n", + " pass\n", + " \n", + " return None\n", + "\n", + "\n", + "def formats_path_information(kg: nx.multidigraph.MultiDiGraph, paths: List, path_type: str, metadata_func: Callable, metadata_dict: Dict, node_metadata: Dict, verbose: bool=False, rand: bool=False, sample_size: int=10) -> None:\n", + " \"\"\"Processes shortest and simple path results.\n", + " \n", + " Args:\n", + " kg: A networkx MultiDiGraph object.\n", + " paths: A nested list of strings, where each string contains an an entity identifier.\n", + " path_type: A string, either 'simple' or 'shortest' that indicates the types of paths to process.\n", + " metadata_func: A function that processes edge metadata.\n", + " metadata_dict: A nested dictionary containing node and edge-level metadata. \n", + " node_metadata: A nested dictionary containing node attributes.\n", + " verbose: A bool indicating whether or not node and edge metadata should be printed.\n", + " rand: A bool indicating whether or not to draw random samples from the path.\n", + " sample_size: An integer used when rand is True to specify the size of the random sample to draw.\n", + " \n", + " Returns:\n", + " None\n", + " \"\"\"\n", + " \n", + " if path_type == 'shortest': \n", + " for i in range(0, len(paths[0]) - 1):\n", + " s = paths[0][i]; o = paths[0][i + 1]\n", + " edges = kg.get_edge_data(*(s, o)).keys()\n", + " for e in edges:\n", + " spe = '\\n' if list(edges).index(e) == 0 else '\\n\\n\\n'\n", + " s, s_label = str(s).split('/')[-1], node_metadata[str(s)]['label']\n", + " e_label = node_metadata[str(e)]['label']\n", + " o, o_label, o_def = str(o).split('/')[-1], node_metadata[str(o)]['label'], node_metadata[str(o)]['description']\n", + " if verbose:\n", + " if o_def != 'None': print(spe + '>>> {} ({}) - {} - {} ({})\\n\\n{} Definition: {}'.format(s_label, s, e_label, o_label, o, o, o_def))\n", + " else: print(spe + '>>> {} ({}) - {} - {} ({})'.format(s_label, s, e_label, o_label, o))\n", + " metadata_func(s, o, metadata_dict)\n", + " else: print('>>> {} ({}) - {} - {} ({})'.format(s_label, s, e_label, o_label, o))\n", + " else:\n", + " if rand: paths = random.sample(paths, sample_size)\n", + " for path in paths:\n", + " print('*' * 100)\n", + " for i in range(0, len(path) - 1):\n", + " spe = '\\n' if i == 0 else '\\n\\n\\n'\n", + " s = path[i]; o = path[i + 1]; edges = kg.get_edge_data(*(s, o))\n", + " try: edges.keys()\n", + " except AttributeError: edges = kg.get_edge_data(*(o, s))\n", + " for e in edges.keys():\n", + " s, s_label = str(s).split('/')[-1], node_metadata[str(s)]['label']\n", + " e_label = node_metadata[str(e)]['label']\n", + " o, o_label, o_def = str(o).split('/')[-1], node_metadata[str(o)]['label'], node_metadata[str(o)]['description']\n", + " if verbose:\n", + " if o_def != 'None': print(spe + '>>> {} ({}) - {} - {} ({})\\n\\n{} Definition: {}'.format(s_label, s, e_label, o_label, o, o, o_def))\n", + " else: print(spe + '>>> {} ({}) - {} - {} ({})'.format(s_label, s, e_label, o_label, o))\n", + " metadata_func(s, o, metadata_dict)\n", + " else: print('>>> {} ({}) - {} - {} ({})'.format(s_label, s, e_label, o_label, o))\n", + " print('*' * 100); print('\\n')\n", + " \n", + " return None\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "\n", + "## Loading Data\n", + "***\n", + "\n", + "___\n", + "\n", + "The knowledge graph will be loaded as a `networkx` MultiDiGraph object and the node labels will be read in and converted to a dictionary to enable easy access to node labels and other relevant metadata." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The knowledge graph contains 780753 nodes and 7787308 edges\n" + ] + } + ], + "source": [ + "# load the knowledge graph\n", + "kg = nx.read_gpickle(write_location + data_urls[0].split('/')[-1])\n", + "print('The knowledge graph contains {} nodes and {} edges'.format(len(kg.nodes()), len(kg.edges())))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# convert multidigraph to undirected graph -- needed to run some of the algorithms\n", + "undirected_kg = kg.to_undirected()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>entity_type</th>\n", + " <th>integer_id</th>\n", + " <th>entity_uri</th>\n", + " <th>label</th>\n", + " <th>description/definition</th>\n", + " <th>synonym</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>NODES</td>\n", + " <td>684158</td>\n", + " <td>https://www.ncbi.nlm.nih.gov/snp/rs864622148</td>\n", + " <td>NM_000051.4(ATM):c.5887G&gt;A (p.Asp1963Asn)</td>\n", + " <td>This variant is a germline single nucleotide v...</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>NODES</td>\n", + " <td>668197</td>\n", + " <td>https://uswest.ensembl.org/Homo_sapiens/Transc...</td>\n", + " <td>CUL9-211</td>\n", + " <td>Transcript CUL9-211 is classified as type 'non...</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>NODES</td>\n", + " <td>769680</td>\n", + " <td>http://purl.obolibrary.org/obo/CHEBI_116891</td>\n", + " <td>3-(3-methylphenyl)-2-sulfanylidene-1H-benzofur...</td>\n", + " <td>None</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>NODES</td>\n", + " <td>381659</td>\n", + " <td>https://www.ncbi.nlm.nih.gov/snp/rs61741688</td>\n", + " <td>NM_001272071.2(AP1S2):c.288T&gt;C (p.Ser96=)</td>\n", + " <td>This variant is a germline single nucleotide v...</td>\n", + " <td>None</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>NODES</td>\n", + " <td>720533</td>\n", + " <td>http://purl.obolibrary.org/obo/PR_Q9Y2I7-3</td>\n", + " <td>1-phosphatidylinositol 3-phosphate 5-kinase is...</td>\n", + " <td>A 1-phosphatidylinositol 3-phosphate 5-kinase ...</td>\n", + " <td>hPIKFYVE/iso:h3</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " entity_type integer_id entity_uri \\\n", + "0 NODES 684158 https://www.ncbi.nlm.nih.gov/snp/rs864622148 \n", + "1 NODES 668197 https://uswest.ensembl.org/Homo_sapiens/Transc... \n", + "2 NODES 769680 http://purl.obolibrary.org/obo/CHEBI_116891 \n", + "3 NODES 381659 https://www.ncbi.nlm.nih.gov/snp/rs61741688 \n", + "4 NODES 720533 http://purl.obolibrary.org/obo/PR_Q9Y2I7-3 \n", + "\n", + " label \\\n", + "0 NM_000051.4(ATM):c.5887G>A (p.Asp1963Asn) \n", + "1 CUL9-211 \n", + "2 3-(3-methylphenyl)-2-sulfanylidene-1H-benzofur... \n", + "3 NM_001272071.2(AP1S2):c.288T>C (p.Ser96=) \n", + "4 1-phosphatidylinositol 3-phosphate 5-kinase is... \n", + "\n", + " description/definition synonym \n", + "0 This variant is a germline single nucleotide v... None \n", + "1 Transcript CUL9-211 is classified as type 'non... None \n", + "2 None None \n", + "3 This variant is a germline single nucleotide v... None \n", + "4 A 1-phosphatidylinositol 3-phosphate 5-kinase ... hPIKFYVE/iso:h3 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# read in node metadata\n", + "node_data = pd.read_csv(write_location + data_urls[1].split('/')[-1], header=0, sep=r\"\\t\", encoding=\"utf8\", engine='python', quoting=3)\n", + "# remove angle brackets\n", + "node_data['entity_uri'] = node_data['entity_uri'].str.strip('<>')\n", + "\n", + "node_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c94990af9fe146f7ae8e5ea649c19504", + "version_major": 2, + "version_minor": 0 + }, + "text/html": [ + "<p>Failed to display Jupyter Widget of type <code>HBox</code>.</p>\n", + "<p>\n", + " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", + " that the widgets JavaScript is still loading. If this message persists, it\n", + " likely means that the widgets JavaScript library is either not installed or\n", + " not enabled. See the <a href=\"https://ipywidgets.readthedocs.io/en/stable/user_install.html\">Jupyter\n", + " Widgets Documentation</a> for setup instructions.\n", + "</p>\n", + "<p>\n", + " If you're reading this message in another notebook frontend (for example, a static\n", + " rendering on GitHub or <a href=\"https://nbviewer.jupyter.org/\">NBViewer</a>),\n", + " it may mean that your frontend doesn't currently support widgets.\n", + "</p>\n" + ], + "text/plain": [ + "HBox(children=(FloatProgress(value=0.0, max=781049.0), HTML(value='')))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "# remove angle brackets\n", + "node_data['entity_uri'] = node_data['entity_uri'].str.strip('<>')\n", + "\n", + "# convert node data to dictionary\n", + "node_data_dict = dict()\n", + "for idx, row in tqdm(node_data.iterrows(), total=node_data.shape[0]):\n", + " node_data_dict[row['entity_uri']] = {\n", + " 'label': row['label'],\n", + " 'description': row['description/definition'],\n", + " 'synonym': row['synonym']\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This file is temporary while the next release is being formatted." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "ename": "FileNotFoundError", + "evalue": "[Errno 2] No such file or directory: '../releases/Columbia_Collaboration/tara_anand/data/entity_metadata_dict.pkl?dl=1'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-10-5aa907383b41>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# load metadata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mfilepath\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwrite_location\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mdata_urls\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'/'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mmax_bytes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0;36m31\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m;\u001b[0m \u001b[0minput_size\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetsize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m;\u001b[0m \u001b[0mbytes_in\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbytearray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'rb'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mf_in\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minput_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_bytes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/genericpath.py\u001b[0m in \u001b[0;36mgetsize\u001b[0;34m(filename)\u001b[0m\n\u001b[1;32m 48\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mgetsize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[0;34m\"\"\"Return the size of a file, reported by os.stat().\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 50\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mst_size\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 51\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 52\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: '../releases/Columbia_Collaboration/tara_anand/data/entity_metadata_dict.pkl?dl=1'" + ] + } + ], + "source": [ + "# load metadata\n", + "filepath = write_location + data_urls[2].split('/')[-1]\n", + "max_bytes = 2**31 - 1; input_size = os.path.getsize(filepath); bytes_in = bytearray(0)\n", + "with open(filepath, 'rb') as f_in:\n", + " for _ in range(0, input_size, max_bytes):\n", + " bytes_in += f_in.read(max_bytes)\n", + "metadata_dict = pickle.loads(bytes_in)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "\n", + "## Knowledge-based Characterization\n", + "***\n", + "____\n", + "\n", + "The goal is to use the knowledge graph to explore what we know about specific concepts as well as what we can say about pairs of concepts. Additional details are presented by comparison below:\n", + "\n", + "#### [Node-Level](#node-level)\n", + " - <u>Node Ancestry</u>: Identify all ancestors for each node up to the root.\n", + " - <u>Node Neighborhood</u>: Returns all nodes reachable from a node of interest via a single directed edge. \n", + "\n", + "\n", + "#### [Path-Level](#path-level)\n", + " - <u>All Shortest Paths</u>: Returns the shortest simple path, if there are multiple paths of the same length then they are all returned.\n", + " - <u>All Simple Paths</u>: A simple path is a path with no repeated nodes. These nodes are identified using a modified depth-first search. Given that there are a lot of these, the initial output is limited to a random draw of 10 paths of length 10 from the first 100 derived paths.\n", + " \n", + "For all comparisons, the full edge is returned along with all relevant node and edge metadata provided by each data source.\n", + "\n", + " ---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Node-Level Characterization <a class=\"anchor\" id=\"node-level\"></a>\n", + "\n", + "This section characterizes the following concepts:\n", + "- [benazepril (`CHEBI_3011`)](#chebi1) \n", + "- [hydrochlorothiazide (`CHEBI_5778`)](#chebi2) \n", + "- [Acute Myocardial Infarction (`MONDO_0004781`)](#mondo1) \n", + "- [Myocardial infarction (`HP_0001658`)](#hpo1)\n", + "\n", + "*Note*. All output is presented twice for each analysis, the first without any metadata/evidence and the second time, with metadata. This is done to facilitate readability.\n", + "\n", + "____\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### benazepril (`CHEBI_3011`) <a class=\"anchor\" id=\"chebi1\"></a>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Ancestors*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# examine the node's ancestors\n", + "prefix = 'CHEBI'; node = [obo.CHEBI_3011]\n", + "path_list = nx_ancestor_search(kg, node.copy(), prefix)\n", + "chebi3011_ancestors = processes_ancestor_path_list(path_list, node_data_dict)\n", + "\n", + "# print results -- nodes are ordered by seniority (higher numbers indicate closer to root)\n", + "print('Ancestors of {}\\n'.format(node[0]))\n", + "for level in range(len(chebi3011_ancestors)):\n", + " print('Level: {}'.format(str(level + 1)))\n", + " for v in chebi3011_ancestors[level]:\n", + " print('\\t- {}'.format(re.sub('http://purl.obolibrary.org/obo/', '', v)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Neighborhood*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# examine the node's neigborhood\n", + "nodes = list(kg.neighbors(node[0]))\n", + "neighbors = [a for b in [[[i, n] for j in [kg.get_edge_data(*(node[0], n)).keys()]\n", + " for i in j] for n in nodes] for a in b]\n", + "chebi3011_sorted_neigbors = sorted(neighbors, key=lambda x: (str(x[1]).split('/')[-1], x[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print nodes without definitions\n", + "formats_node_information(chebi3011_sorted_neigbors, node_data_dict, verbose=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print nodes with definitions\n", + "formats_node_information(chebi3011_sorted_neigbors, node_data_dict, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "**hydrochlorothiazide (`CHEBI_5778`)** <a class=\"anchor\" id=\"chebi2\"></a>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Ancestors*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# examine the node's ancestors\n", + "prefix = 'CHEBI'; node = [obo.CHEBI_5778]\n", + "path_list = nx_ancestor_search(kg, node.copy(), prefix)\n", + "chebi5778_ancestors = processes_ancestor_path_list(path_list, node_data_dict)\n", + "\n", + "# print results -- nodes are ordered by seniority (higher numbers indicate closer to root)\n", + "print('Ancestors of {}\\n'.format(node[0]))\n", + "for level in range(len(chebi5778_ancestors)):\n", + " print('Level: {}'.format(str(level + 1)))\n", + " for v in chebi5778_ancestors[level]:\n", + " print('\\t- {}'.format(re.sub('http://purl.obolibrary.org/obo/', '', v)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Neighborhood*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# examine the node's neigborhood\n", + "nodes = list(kg.neighbors(node[0]))\n", + "neighbors = [a for b in [[[i, n] for j in [kg.get_edge_data(*(node[0], n)).keys()]\n", + " for i in j] for n in nodes] for a in b]\n", + "chebi5778_sorted_neigbors = sorted(neighbors, key=lambda x: (str(x[1]).split('/')[-1], x[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print nodes without definitions\n", + "formats_node_information(chebi5778_sorted_neigbors, node_data_dict, verbose=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print nodes with definitions\n", + "formats_node_information(chebi5778_sorted_neigbors, node_data_dict, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "**Myocardial Infarction (`MONDO_0005068`)** <a class=\"anchor\" id=\"mondo1\"></a>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Ancestors*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# examine the node's ancestors\n", + "prefix = 'MONDO'; node = [obo.MONDO_0005068]\n", + "path_list = nx_ancestor_search(kg, node.copy(), prefix)\n", + "mondo0005068_ancestors = processes_ancestor_path_list(path_list, node_data_dict)\n", + "\n", + "# print results -- nodes are ordered by seniority (higher numbers indicate closer to root)\n", + "print('Ancestors of {}\\n'.format(node[0]))\n", + "for level in range(len(mondo0005068_ancestors)):\n", + " print('Level: {}'.format(str(level + 1)))\n", + " for v in mondo0005068_ancestors[level]:\n", + " print('\\t- {}'.format(re.sub('http://purl.obolibrary.org/obo/', '', v)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Neighborhood*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# examine the node's neigborhood\n", + "nodes = list(kg.neighbors(node[0]))\n", + "neighbors = [a for b in [[[i, n] for j in [kg.get_edge_data(*(node[0], n)).keys()]\n", + " for i in j] for n in nodes] for a in b]\n", + "mondo0005068_sorted_neigbors = sorted(neighbors, key=lambda x: (str(x[1]).split('/')[-1], x[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print nodes without definitions\n", + "formats_node_information(mondo0005068_sorted_neigbors, node_data_dict, verbose=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print nodes with definitions\n", + "formats_node_information(mondo0005068_sorted_neigbors, node_data_dict, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "**Myocardial infarction (`HP_0001658`)** <a class=\"anchor\" id=\"hpo1\"></a>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Ancestors*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# examine the node's ancestors\n", + "prefix = 'HP'; node = [obo.HP_0001658]\n", + "path_list = nx_ancestor_search(kg, node.copy(), prefix)\n", + "hp0001658_ancestors = processes_ancestor_path_list(path_list, node_data_dict)\n", + "\n", + "# print results -- nodes are ordered by seniority (higher numbers indicate closer to root)\n", + "print('Ancestors of {}\\n'.format(node[0]))\n", + "for level in range(len(hp0001658_ancestors)):\n", + " print('Level: {}'.format(str(level + 1)))\n", + " for v in hp0001658_ancestors[level]:\n", + " print('\\t- {}'.format(re.sub('http://purl.obolibrary.org/obo/', '', v)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Neighborhood*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# examine the node's neigborhood\n", + "nodes = list(kg.neighbors(node[0]))\n", + "neighbors = [a for b in [[[i, n] for j in [kg.get_edge_data(*(node[0], n)).keys()]\n", + " for i in j] for n in nodes] for a in b]\n", + "hp0001658_sorted_neigbors = sorted(neighbors, key=lambda x: (str(x[1]).split('/')[-1], x[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print nodes without definitions\n", + "formats_node_information(hp0001658_sorted_neigbors, node_data_dict, verbose=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print nodes with definitions\n", + "formats_node_information(hp0001658_sorted_neigbors, node_data_dict, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "___\n", + "\n", + "### Path-Level Characterization <a class=\"anchor\" id=\"path-level\"></a>\n", + "\n", + "This section characterizes the following concept pairs:\n", + "- [benazepril (`CHEBI_3011`) - Myocardial Infarction (`MONDO_0005068`)](#pair1) \n", + "- [hydrochlorothiazide (`CHEBI_5778`) - Myocardial Infarction (`MONDO_0005068`)](#pair2) \n", + "- [benazepril (`CHEBI_3011`) - Myocardial infarction (`HP_0001658`)](#pair3) \n", + "- [hydrochlorothiazide (`CHEBI_5778`) - Myocardial infarction (`HP_0001658`)](#pair4) \n", + "\n", + "*Note*. All output is presented twice for each analysis, the first without any metadata/evidence and the second time, with metadata. This is done to facilitate readability.\n", + "___" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**benazepril (`CHEBI_3011`) - Myocardial Infarction (`MONDO_0005068`)** <a class=\"anchor\" id=\"pair1\"></a>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Shortest Paths*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# look at all shortest paths between the nodes in pair\n", + "shortest_paths = list(nx.all_shortest_paths(kg, obo.CHEBI_3011, obo.MONDO_0005068))\n", + "formats_path_information(kg, shortest_paths, path_type='shortest', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Simple Paths*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# look at all simple paths between the nodes in pair\n", + "simple_paths = []; counter = 0\n", + "for path in tqdm(nx.all_simple_paths(undirected_kg, source=obo.CHEBI_3011, target=obo.MONDO_0005068, cutoff=10)):\n", + " simple_paths += [path]\n", + " if counter == 100: break\n", + " else: counter += 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print path information -- without definitions and metadata\n", + "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=False, rand=True, sample_size=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# print path information -- with definitions and metadata\n", + "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True, rand=True, sample_size=10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "**hydrochlorothiazide (`CHEBI_5778`) - Myocardial Infarction (`MONDO_0005068`)** <a class=\"anchor\" id=\"pair2\"></a>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Shortest Paths*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# look at all shortest paths between the nodes in pair\n", + "shortest_paths = list(nx.all_shortest_paths(kg, obo.CHEBI_5778, obo.MONDO_0005068))\n", + "formats_path_information(kg, shortest_paths, path_type='shortest', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Simple Paths*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# look at all simple paths between the nodes in pair\n", + "simple_paths = []; counter = 0\n", + "for path in tqdm(nx.all_simple_paths(undirected_kg, source=obo.CHEBI_5778, target=obo.MONDO_0005068, cutoff=10)):\n", + " simple_paths += [path]\n", + " if counter == 100: break\n", + " else: counter += 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print path information -- without definitions and metadata\n", + "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=False, rand=True, sample_size=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print path information -- with definitions and metadata\n", + "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True, rand=True, sample_size=10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "**benazepril (`CHEBI_3011`) - Myocardial infarction (`HP_0001658`)** <a class=\"anchor\" id=\"pair3\"></a>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Shortest Paths*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# look at all shortest paths between the nodes in pair\n", + "shortest_paths = list(nx.all_shortest_paths(kg, obo.CHEBI_3011, obo.HP_0001658))\n", + "formats_path_information(kg, shortest_paths, path_type='shortest', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Simple Paths*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# look at all simple paths between the nodes in pair\n", + "simple_paths = []; counter = 0\n", + "for path in tqdm(nx.all_simple_paths(undirected_kg, source=obo.CHEBI_3011, target=obo.HP_0001658, cutoff=10)):\n", + " simple_paths += [path]\n", + " if counter == 100: break\n", + " else: counter += 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# print path information -- without definitions and metadata\n", + "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=False, rand=True, sample_size=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# print path information -- with definitions and metadata\n", + "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True, rand=True, sample_size=10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<br>\n", + "\n", + "**hydrochlorothiazide (`CHEBI_5778`) - Myocardial infarction (`HP_0001658`)** <a class=\"anchor\" id=\"pair4\"></a>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Shortest Paths*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# look at all shortest paths between the nodes in pair\n", + "shortest_paths = list(nx.all_shortest_paths(kg, obo.CHEBI_5778, obo.HP_0001658))\n", + "formats_path_information(kg, shortest_paths, path_type='shortest', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Simple Paths*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# look at all simple paths between the nodes in pair\n", + "simple_paths = []; counter = 0\n", + "for path in tqdm(nx.all_simple_paths(undirected_kg, source=obo.CHEBI_5778, target=obo.HP_0001658, cutoff=10)):\n", + " simple_paths += [path]\n", + " if counter == 100: break\n", + " else: counter += 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# print path information -- without definitions and metadata\n", + "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=False, rand=True, sample_size=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# print path information -- with definitions and metadata\n", + "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True, rand=True, sample_size=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From facfc774425e79c56b979d022e1e849af57f04fb Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:35:46 -0400 Subject: [PATCH 093/112] suppressing biolink functions need to strongly consider whether to add biolink dependency --- pkt_kg/utils/__init__.py | 4 +- pkt_kg/utils/data_utils.py | 98 ++++++++++++------------ tests/test_data_utils_miscellaneous.py | 101 ++++++++++++------------- 3 files changed, 102 insertions(+), 101 deletions(-) diff --git a/pkt_kg/utils/__init__.py b/pkt_kg/utils/__init__.py index cdc827da..d055e400 100644 --- a/pkt_kg/utils/__init__.py +++ b/pkt_kg/utils/__init__.py @@ -9,7 +9,9 @@ __all__ = ['adds_edges_to_graph', 'adds_namespace_to_bnodes', 'appends_to_existing_file', 'chunks', 'connected_components', 'convert_to_networkx', 'data_downloader', 'deduplicates_file', 'derives_graph_statistics', 'dump_jsonl', 'explodes_data', 'finds_node_type', 'ftp_url_download', - 'genomic_id_mapper', 'gets_biolink_information', 'gets_deprecated_ontology_classes', + 'genomic_id_mapper', + # 'gets_biolink_information', + 'gets_deprecated_ontology_classes', 'gets_entity_ancestors', 'gets_object_properties', 'gets_ontology_class_dbxrefs', 'gets_ontology_class_synonyms', 'gets_ontology_classes', 'gets_ontology_definitions', 'gets_ontology_statistics', 'gzipped_ftp_url_download', 'gzipped_url_download', 'load_jsonl', diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index b80e3680..36bb5866 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -546,55 +546,55 @@ def obtains_entity_url(prefix: str, identifier: Union[int, str], url: Optional[s return entity_url -def gets_biolink_information(entity: str, entity_label: Optional[str] = None, biolink_loc='./resources/') -> str: - """Function takes an entity CURIE and label and returns its BioLink Model type. First, the function uses the - TranslatorSRI API. If that does not return a match, the function then downloads (if not already downloaded) a - yaml file of the current BioLink model and searches it. If that also does not return a match, then the function - formats the entity's label and returns it as the biolink type. Examples are shown below. For entities, - this function relies on the TranslatorSRI application (https://github.com/TranslatorSRI/NodeNormalization). - - Assumptions: If more than 1 BioLink type is provided, the function is designed to take the first one. - - EXAMPLE OUTPUT: - - ""CHEBI:16753" --> biolink:SmallMolecule - - "RO:0002512" --> biolink:translation_of - - Args: - entity: A string containing an entity CURIE (e.g., CHEBI:16753) or None. - entity_label: A string representing an entity label. - biolink_loc: A string containing a location to a biolink yaml file. - - Returns: - biolink_type: A string containing a BioLink model type for a node or an predication. - """ - - # check for biolink data being downloaded - biolink_file = 'https://raw.githubusercontent.com/biolink/biolink-model/master/biolink-model.yaml' - if not os.path.exists(biolink_loc + 'biolink-model.yaml'): data_downloader(biolink_file, biolink_loc) - biolink_data = yaml.load(open(biolink_loc + 'biolink-model.yaml'), Loader=yaml.FullLoader) - - # find entities bioLink type - entity = entity.replace('_', ':') - result = requests.get('https://nodenormalization-sri.renci.org/get_normalized_nodes', params={'curie': entity}) - res = result.json() - - if res[entity] is not None: biolink_type = res[entity]['type'][0] - else: # checks the biolink yaml for the entity CURIE - temp_idx = [k for k in biolink_data['slots'].keys() - if ('exact_mappings' in biolink_data['slots'][k].keys() - and entity in biolink_data['slots'][k]['exact_mappings']) - or ('narrow_mappings' in biolink_data['slots'][k].keys() - and entity in biolink_data['slots'][k]['narrow_mappings'])] - if len(temp_idx) > 0: biolink_type = 'biolink:{}'.format(temp_idx[0].replace(' ', '_')) - else: # checks the biolink yaml for the entity label - if entity_label is not None: - entity_label = entity_label.lower() - temp_str = [k for k in biolink_data['slots'].keys() if k == entity_label] - if len(temp_str) > 0: biolink_type = 'biolink:{}'.format(temp_str[0].replace(' ', '_')) - else: biolink_type = 'biolink:{}'.format(entity_label.replace(' ', '_')) - else: biolink_type = 'biolink:Other' - - return biolink_type +# def gets_biolink_information(entity: str, entity_label: Optional[str] = None, biolink_loc='./resources/') -> str: +# """Function takes an entity CURIE and label and returns its BioLink Model type. First, the function uses the +# TranslatorSRI API. If that does not return a match, the function then downloads (if not already downloaded) a +# yaml file of the current BioLink model and searches it. If that also does not return a match, then the function +# formats the entity's label and returns it as the biolink type. Examples are shown below. For entities, +# this function relies on the TranslatorSRI application (https://github.com/TranslatorSRI/NodeNormalization). +# +# Assumptions: If more than 1 BioLink type is provided, the function is designed to take the first one. +# +# EXAMPLE OUTPUT: +# - ""CHEBI:16753" --> biolink:SmallMolecule +# - "RO:0002512" --> biolink:translation_of +# +# Args: +# entity: A string containing an entity CURIE (e.g., CHEBI:16753) or None. +# entity_label: A string representing an entity label. +# biolink_loc: A string containing a location to a biolink yaml file. +# +# Returns: +# biolink_type: A string containing a BioLink model type for a node or an predication. +# """ +# +# # check for biolink data being downloaded +# biolink_file = 'https://raw.githubusercontent.com/biolink/biolink-model/master/biolink-model.yaml' +# if not os.path.exists(biolink_loc + 'biolink-model.yaml'): data_downloader(biolink_file, biolink_loc) +# biolink_data = yaml.load(open(biolink_loc + 'biolink-model.yaml'), Loader=yaml.FullLoader) +# +# # find entities bioLink type +# entity = entity.replace('_', ':') +# result = requests.get('https://nodenormalization-sri.renci.org/get_normalized_nodes', params={'curie': entity}) +# res = result.json() +# +# if res[entity] is not None: biolink_type = res[entity]['type'][0] +# else: # checks the biolink yaml for the entity CURIE +# temp_idx = [k for k in biolink_data['slots'].keys() +# if ('exact_mappings' in biolink_data['slots'][k].keys() +# and entity in biolink_data['slots'][k]['exact_mappings']) +# or ('narrow_mappings' in biolink_data['slots'][k].keys() +# and entity in biolink_data['slots'][k]['narrow_mappings'])] +# if len(temp_idx) > 0: biolink_type = 'biolink:{}'.format(temp_idx[0].replace(' ', '_')) +# else: # checks the biolink yaml for the entity label +# if entity_label is not None: +# entity_label = entity_label.lower() +# temp_str = [k for k in biolink_data['slots'].keys() if k == entity_label] +# if len(temp_str) > 0: biolink_type = 'biolink:{}'.format(temp_str[0].replace(' ', '_')) +# else: biolink_type = 'biolink:{}'.format(entity_label.replace(' ', '_')) +# else: biolink_type = 'biolink:Other' +# +# return biolink_type def dump_jsonl(data: List, output_path: str) -> None: diff --git a/tests/test_data_utils_miscellaneous.py b/tests/test_data_utils_miscellaneous.py index f7cfe1f6..ab11e52b 100644 --- a/tests/test_data_utils_miscellaneous.py +++ b/tests/test_data_utils_miscellaneous.py @@ -4,7 +4,6 @@ import shutil import unittest -from tqdm import tqdm from typing import List from pkt_kg.utils import * @@ -201,54 +200,54 @@ def tests_obtains_entity_url_bad2(self): return None - def tests_gets_biolink_information_entity(self): - """Tests the gets_biolink_information function when provided a valid entity CURIE.""" - - # set-up input - entity = 'CHEBI:16753'; entity_label = None - - # test function - res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') - self.assertEqual(res, 'biolink:SmallMolecule') - - return None - - def tests_gets_biolink_information_entitylabel(self): - """Tests the gets_biolink_information function when provided a valid entity CURIE and label are provided.""" - - # set-up input - entity = 'RO:0002436'; entity_label = 'molecularly interacts with' - - # test function - res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') - self.assertEqual(res, 'biolink:molecularly_interacts_with') - - return None - - def tests_gets_biolink_information_entitylabel2(self): - """Tests the gets_biolink_information function when provided a valid entity CURIE and label are provided.""" - - # set-up input - entity = 'rdfs:subClassOf'; entity_label = 'subclass of' - - # test function - res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') - self.assertEqual(res, 'biolink:subclass_of') - - return None - - def tests_gets_biolink_information_entitylabel3(self): - """Tests the gets_biolink_information function when provided a valid entity CURIE and label that cannot be - found in the model or API.""" - - # set-up input - entity = 'RO:0000000'; entity_label = None - - # test function - res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') - self.assertEqual(res, 'biolink:Other') - - return None + # def tests_gets_biolink_information_entity(self): + # """Tests the gets_biolink_information function when provided a valid entity CURIE.""" + # + # # set-up input + # entity = 'CHEBI:16753'; entity_label = None + # + # # test function + # res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') + # self.assertEqual(res, 'biolink:SmallMolecule') + # + # return None + # + # def tests_gets_biolink_information_entitylabel(self): + # """Tests the gets_biolink_information function when provided a valid entity CURIE and label are provided.""" + # + # # set-up input + # entity = 'RO:0002436'; entity_label = 'molecularly interacts with' + # + # # test function + # res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') + # self.assertEqual(res, 'biolink:molecularly_interacts_with') + # + # return None + # + # def tests_gets_biolink_information_entitylabel2(self): + # """Tests the gets_biolink_information function when provided a valid entity CURIE and label are provided.""" + # + # # set-up input + # entity = 'rdfs:subClassOf'; entity_label = 'subclass of' + # + # # test function + # res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') + # self.assertEqual(res, 'biolink:subclass_of') + # + # return None + # + # def tests_gets_biolink_information_entitylabel3(self): + # """Tests the gets_biolink_information function when provided a valid entity CURIE and label that cannot be + # found in the model or API.""" + # + # # set-up input + # entity = 'RO:0000000'; entity_label = None + # + # # test function + # res = gets_biolink_information(entity, entity_label, self.dir_loc + '/') + # self.assertEqual(res, 'biolink:Other') + # + # return None def tests_dump_jsonl(self): """Tests the dump_jsonl function.""" @@ -277,8 +276,8 @@ def tests_load_jsonl(self): # test function data_dict = load_jsonl(out_location) - test_dict = {'https://chordanalytics.ca/': {'status_code': 200}, - 'https://github.com/agalea91': {'status_code': 200}} + test_dict = {'https://chordanalytics.ca/': "{'status_code': 200}", + 'https://github.com/agalea91': "{'status_code': 200}"} self.assertIsInstance(data_dict, dict) self.assertEqual(data_dict, test_dict) From e3790b47c49897a9b3a2f29890c7cec8f7ba1114 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:55:53 -0400 Subject: [PATCH 094/112] adding back org test data --- notebooks/Entity_Search_Examples.ipynb | 1 + tests/data/edge_source_list.txt | 8 +----- tests/data/ontology_source_list.txt | 8 +----- tests/data/resource_info.txt | 35 ++------------------------ 4 files changed, 5 insertions(+), 47 deletions(-) diff --git a/notebooks/Entity_Search_Examples.ipynb b/notebooks/Entity_Search_Examples.ipynb index c68ab6c6..d7eefcfa 100644 --- a/notebooks/Entity_Search_Examples.ipynb +++ b/notebooks/Entity_Search_Examples.ipynb @@ -148,6 +148,7 @@ "execution_count": null, "metadata": { "code_folding": [ + 0, 28, 54, 80, diff --git a/tests/data/edge_source_list.txt b/tests/data/edge_source_list.txt index e0265fa4..19407f1e 100644 --- a/tests/data/edge_source_list.txt +++ b/tests/data/edge_source_list.txt @@ -1,7 +1 @@ -##################################################################################################################################################### -#### edge_source_info.txt (last updated: December 27, 2021) -### Each column is separated by a pipe (i.e., "|") and includes the following: -# EdgeType: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and ontology_source_list.txt files. -# URL: A string containing a URL to the primary data source for the edge. -##################################################################################################################################################### -chemical-disease|http://ctdbase.org/reports/CTD_chemicals_diseases.tsv.gz \ No newline at end of file +chemical-disease, http://ctdbase.org/reports/CTD_chemicals_diseases.tsv.gz \ No newline at end of file diff --git a/tests/data/ontology_source_list.txt b/tests/data/ontology_source_list.txt index ccd98081..a354f197 100644 --- a/tests/data/ontology_source_list.txt +++ b/tests/data/ontology_source_list.txt @@ -1,7 +1 @@ -################################################################################################################################################### -#### ontology_source_info.txt (last updated: December 27, 2021) -### Each column is separated by a pipe (i.e., "|") and includes the following: -# Ontology: A string label for an edge (node1-node2). The label matches what is used in the resource_info.txt and edge_source_list.txt files. -# URL: A string containing a URL to the ontology file. -#################################################################################################################################################### -phenotype|http://purl.obolibrary.org/obo/hp.owl \ No newline at end of file +phenotype, http://purl.obolibrary.org/obo/hp.owl \ No newline at end of file diff --git a/tests/data/resource_info.txt b/tests/data/resource_info.txt index 577badaf..6d2ad1cd 100644 --- a/tests/data/resource_info.txt +++ b/tests/data/resource_info.txt @@ -1,33 +1,2 @@ -###################################################################################################################################################################################### -#### resource_info.txt (last updated: December 27, 2021) -### Each column is separated by a pipe (i.e., "|") and includes the following: -# EdgeType: A string label for an edge (node1-node2). The label matches what is used in the edge_source_list.txt and ontology_source_list.txt files. -# IdentifierPrefixInformation: Three ";"-separated items used to update a prefix-identifier pair (e.g., :;GO_;GO_). The first item contains the character that separates -# existing prefixes and identifiers (e.g. ":" in GO:1283834). The second item contains the current prefix and the third item contains the -# new prefix (i.e. 'GO_' and 'GO_'). If the existing prefix is correct, type ";;". if there is no prefix in the current data, leave the -# item empty and specify the new prefix for the node in the corresponding item location. -# NodeDataTypes: A label of "class" or "entity" for each node in an edge separated by "-" (e.g., "class-class"). The "class" label represents nodes from -# ontologies and "entity" represents nodes from other data sources. -# Relation: A Relation Ontology (http://www.obofoundry.org/ontology/ro.html) CURIE (e.g., RO_0000056). -# Delimiter: A character used to split rows from an input data source into columns (e.g., "t" for tab-delimited data or "," for comma-delimited data). -# ColumnIndexes: Two-column indexes separated by ";" (e.g., "0;4" for the first and third columns in the input data source). -# IdentifierMaps: A string of mapping information for each node in an edge. For example, the string "2:mapping_file_1.txt;4:mapping_file_2.txt" means that -# the first node requires data contained in the 2nd column of the "mapping_file_1.txt" and the second node requires data from the 4th column -# in the "mapping_file_2.txt" file. -# EvidenceCriteria: Evidence criteria that can be used to filter an input data source (e.g., scores above a certain cut-off). An evidence set is composed of 3 -# pieces of ";"-separated information. Multiple evidence sets can be passed, where each set is separated by "::". Consider the following -# example: "4;!=;IEA::8;<;0.0001": -# 1. The index of the column to apply the evidence criteria to (e.g., "4" and "8" in the example above) -# 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "!=" and "<" -# in the example above) -# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "IEA" and "0.0001" in the example above) -# FilterCriteria: Filtering criteria that can be used to filter an input data source (e.g., human proteins). An evidence set is composed of 3 pieces of ";"- -# separated information. Multiple filtering sets can be passed, where each set is separated by "::". Consider the following example: -# "5;==;P::7;==;9606"): -# 1. The index of the column to apply the evidence criteria to (e.g., "5" and "7" in the example above) -# 2. The operator (i.e., "==", "!=", "<", ">", "<=", ">=", "in", ".startswith()", ".endswith()") to use when filtering (e.g., "==" and "==" -# in the example above) -# 3. The value (i.e., "int", "float", "str", "list") to filter on (e.g., "P" and "9606" in the example above) -###################################################################################################################################################################################### -chemical-disease|:;MESH_;|class-class|RO_0002606|t|1;4|0:MESH_CHEBI_MAP.txt;1:DISEASE_DOID_MAP.txt|5;!=;''|None -gene-disease|;;|entity-class|RO_0003302|t|0;4|1:DISEASE_DOID_MAP.txt|10;>=;0.70|None \ No newline at end of file +chemical-disease|:;MESH_;|class-class|RO_0002606|http://purl.obolibrary.org/obo/|http://purl.obolibrary.org/obo/|t|1;4|0:MESH_CHEBI_MAP.txt;1:DISEASE_DOID_MAP.txt|5;!=;''|None +gene-disease|;;|entity-class|RO_0003302|http://purl.uniprot.org/geneid/|http://purl.obolibrary.org/obo/|t|0;4|1:DISEASE_DOID_MAP.txt|10;>=;0.70|None \ No newline at end of file From 4fe4f3bd52e450818df0d321c35fd51eee4e1ebb Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 12:58:11 -0400 Subject: [PATCH 095/112] suppressing changes for testing --- pkt_kg/edge_list.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pkt_kg/edge_list.py b/pkt_kg/edge_list.py index 3c26e529..d3b2d973 100755 --- a/pkt_kg/edge_list.py +++ b/pkt_kg/edge_list.py @@ -51,17 +51,19 @@ def __init__(self, data_files: Dict[str, str], source_file: str) -> None: self.source_info: Dict[str, Dict[str, Any]] = dict() with open(source_file, 'r') as source_file_data: - for row in [x for x in source_file_data.read().splitlines() if not x.startswith('#')]: + for row in source_file_data.read().splitlines(): cols = ['"{}"'.format(x.strip()) for x in list(csv.reader([row], delimiter='|', quotechar='"'))[0]] key = cols[0].strip('"').strip("'") self.source_info[key] = {} - self.source_info[key]['identifier_prefix_information'] = cols[1].strip('"').strip("'") - self.source_info[key]['relation'] = cols[2].strip('"').strip("'") - self.source_info[key]['delimiter'] = cols[3].strip('"').strip("'") - self.source_info[key]['column_indexes'] = cols[4].strip('"').strip("'") - self.source_info[key]['identifier_maps'] = cols[5].strip('"').strip("'") - self.source_info[key]['evidence_criteria'] = cols[6].strip('"').strip("'") - self.source_info[key]['filter_criteria'] = cols[7].strip('"').strip("'") + self.source_info[key]['source_labels'] = cols[1].strip('"').strip("'") + self.source_info[key]['data_type'] = cols[2].strip('"').strip("'") + self.source_info[key]['edge_relation'] = cols[3].strip('"').strip("'") + self.source_info[key]['uri'] = (cols[4].strip('"').strip("'"), cols[5].strip('"').strip("'")) + self.source_info[key]['delimiter'] = cols[6].strip('"').strip("'") + self.source_info[key]['column_idx'] = cols[7].strip('"').strip("'") + self.source_info[key]['identifier_maps'] = cols[8].strip('"').strip("'") + self.source_info[key]['evidence_criteria'] = cols[9].strip('"').strip("'") + self.source_info[key]['filter_criteria'] = cols[10].strip('"').strip("'") self.source_info[key]['edge_list'] = [] source_file_data.close() From 99786372111443fe821a5fdbbbbd9d94745f9b25 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 13:09:13 -0400 Subject: [PATCH 096/112] suppressing for testing --- pkt_kg/metadata.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pkt_kg/metadata.py b/pkt_kg/metadata.py index b4b98a49..b1c106df 100644 --- a/pkt_kg/metadata.py +++ b/pkt_kg/metadata.py @@ -36,7 +36,6 @@ class Metadata(object): """Class helps manage knowledge graph metadata. - Attributes: kg_version: A string that contains the version of the knowledge graph build. write_location: A filepath to the knowledge graph directory (e.g. './resources/knowledge_graphs). @@ -71,7 +70,6 @@ def metadata_processor(self) -> None: """Loads a directory of node and relations data. The dictionary is nested with the outer keys corresponding to the metadata type (i.e. "nodes" or "relations") and the values containing dictionaries keyed by URI and values containing a dictionary of metadata. - Returns: None. """ @@ -105,10 +103,8 @@ def extract_metadata(self, graph: Graph) -> None: owl:ObjectProperty). Each metadata type is saved as a dictionary key with the actual string stored as the value. The metadata types are packaged as a dictionary which is stored as the value to the node identifier as the key. - Args: graph: An rdflib graph object. - Returns: None. """ @@ -160,12 +156,10 @@ def creates_node_metadata(self, ent: List, e_type: Optional[List] = None, key_ty """Given a node in the knowledge graph, if the node is not an ontology class and if it has metadata information, then new edges are created to add the metadata to the knowledge graph. Metadata that is added includes: labels, descriptions, and synonyms. - Args: ent: A list of two node identifiers (e.g. ['http://example/3075', 'http://example/1080']). e_type: A list of types for each node in nodes (e.g. ['entity', 'entity']). key_type: A string indicating if the key should be 'nodes' or 'relations (default='nodes'). - Returns: edges: A list of tuples containing RDFLib objects used to add metadata to a knowledge graph. """ @@ -199,11 +193,9 @@ def creates_node_metadata(self, ent: List, e_type: Optional[List] = None, key_ty def adds_ontology_annotations(self, filename: str, graph: Graph) -> Graph: """Updates the ontology annotation information for an input knowledge graph or ontology. - Args: filename: A string containing the name of a knowledge graph. graph: An rdflib graph object. - Returns: graph: An rdflib graph object with edited ontology annotations. """ @@ -238,16 +230,13 @@ def output_metadata(self, node_integer_map: Dict, graph: Union[Set, Graph]) -> N """Loops over the self.node_dict dictionary and writes out the data to a file locally. The data is stored as a tab-delimited '.txt' file with four columns: (1) node identifier; (2) node label; (3) node description or definition; and (4) node synonym. - NOTE. Not every node in the knowledge class will have metadata. There are some non-ontology nodes that are added (e.g. Ensembl transcript identifiers) that at the time of adding did not include labels, synonyms, or definitions. While these nodes have valid metadata through their original provider, this data may not have been available for download and thus would not have been added to the node_dict. - Args: node_integer_map: A dictionary where keys are integers and values are node and relation identifiers. graph: A set of RDFLib Graph object triples or an RDFLib Graph. - Returns: None. """ From 1a5bebd5bd4b60ba88c516217fda4a94ac017282 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 13:11:23 -0400 Subject: [PATCH 097/112] testing rollback --- tests/test_metadata.py | 62 +++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 11d5dd7f..ec64866f 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -85,33 +85,33 @@ def test_creates_entity_metadata_nodes(self): self.metadata.extract_metadata(self.graph) # test when the node has metadata - updated_graph_1 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', - 'http://www.ncbi.nlm.nih.gov/gene/2'], - e_type=['entity', 'entity']) + updated_graph_1 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', + 'http://www.ncbi.nlm.nih.gov/gene/2'], + e_type=['entity', 'entity']) self.assertTrue(len(updated_graph_1) == 16) # check that the correct info is returned if only one is an entity - updated_graph_2 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', - 'http://www.ncbi.nlm.nih.gov/gene/2'], - e_type=['entity', 'class']) + updated_graph_2 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', + 'http://www.ncbi.nlm.nih.gov/gene/2'], + e_type=['entity', 'class']) self.assertTrue(len(updated_graph_2) == 8) # check that nothing is returned if the entities are classes - updated_graph_3 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', - 'http://www.ncbi.nlm.nih.gov/gene/2'], - e_type=['class', 'class']) + updated_graph_3 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', + 'http://www.ncbi.nlm.nih.gov/gene/2'], + e_type=['class', 'class']) self.assertTrue(updated_graph_3 is None) # test when the node does not have metadata - updated_graph_4 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', - 'http://www.ncbi.nlm.nih.gov/gene/None'], - e_type=['entity', 'entity']) + updated_graph_4 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', + 'http://www.ncbi.nlm.nih.gov/gene/None'], + e_type=['entity', 'entity']) self.assertTrue(updated_graph_4 is None) # test when node_data is None self.metadata.node_data = None - updated_graph_5 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', - 'http://www.ncbi.nlm.nih.gov/gene/None'], - e_type=['entity', 'entity']) + updated_graph_5 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', + 'http://www.ncbi.nlm.nih.gov/gene/None'], + e_type=['entity', 'entity']) self.assertTrue(updated_graph_5 is None) return None @@ -123,18 +123,18 @@ def test_creates_entity_metadata_relations(self): self.metadata.extract_metadata(self.graph) # test when the node has metadata - updated_graph_1 = self.metadata.creates_entity_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002310'], - key_type='relations') + updated_graph_1 = self.metadata.creates_node_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002310'], + key_type='relations') self.assertTrue(len(updated_graph_1) == 2) # check that nothing is returned if the entities are classes - updated_graph_2 = self.metadata.creates_entity_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002597'], - e_type=['class'], key_type='relations') + updated_graph_2 = self.metadata.creates_node_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002597'], + e_type=['class'], key_type='relations') self.assertTrue(len(updated_graph_2) == 2) # test when the node does not have metadata - updated_graph_3 = self.metadata.creates_entity_metadata(['http://www.ncbi.nlm.nih.gov/gene/None'], - key_type='relations') + updated_graph_3 = self.metadata.creates_node_metadata(['http://www.ncbi.nlm.nih.gov/gene/None'], + key_type='relations') self.assertTrue(updated_graph_3 is None) return None @@ -147,25 +147,25 @@ def test_creates_entity_metadata_none(self): self.metadata.node_dict = None # relations -- with valid input - updated_graph_1 = self.metadata.creates_entity_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002597'], - key_type='relations') + updated_graph_1 = self.metadata.creates_node_metadata(ent=['http://purl.obolibrary.org/obo/RO_0002597'], + key_type='relations') self.assertTrue(updated_graph_1 is None) # relations -- without valid input - updated_graph_2 = self.metadata.creates_entity_metadata(ent=['http://purl.obolibrary.org/obo/None'], - key_type='relations') + updated_graph_2 = self.metadata.creates_node_metadata(ent=['http://purl.obolibrary.org/obo/None'], + key_type='relations') self.assertTrue(updated_graph_2 is None) # nodes -- with valid input - updated_graph_3 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', - 'http://www.ncbi.nlm.nih.gov/gene/2'], - e_type=['class', 'class']) + updated_graph_3 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/1', + 'http://www.ncbi.nlm.nih.gov/gene/2'], + e_type=['class', 'class']) self.assertTrue(updated_graph_3 is None) # nodes -- without valid input - updated_graph_4 = self.metadata.creates_entity_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', - 'http://www.ncbi.nlm.nih.gov/gene/None'], - e_type=['entity', 'entity']) + updated_graph_4 = self.metadata.creates_node_metadata(ent=['http://www.ncbi.nlm.nih.gov/gene/None', + 'http://www.ncbi.nlm.nih.gov/gene/None'], + e_type=['entity', 'entity']) self.assertTrue(updated_graph_4 is None) return None From acc57315f0c63b9a62ab524e94bf16384a70698a Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 13:38:32 -0400 Subject: [PATCH 098/112] rollback for testing --- pkt_kg/downloads.py | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/pkt_kg/downloads.py b/pkt_kg/downloads.py index e1d4dacf..ca2a23d2 100644 --- a/pkt_kg/downloads.py +++ b/pkt_kg/downloads.py @@ -40,11 +40,9 @@ class DataSource(object): important information on each of the files that is downloaded. - The class has two subclasses which inherit its methods. Each subclass contains an altered version of the primary classes methods that are specialized for that specific data type. - Attributes: data_path: A string file path/name to a text file storing URLs of different sources to download. resource_data: A string pointing to a data file that contains the contents of resource_info. - Raises: TypeError: If the file pointed to by data_path is not type str. IOError: If the file pointed to by data_path does not exist. @@ -80,8 +78,7 @@ def __init__(self, data_path: str, resource_data: Optional[str] = None) -> None: raise TypeError(log_str) else: resource_data_file: TextIO = open(self.resource_data) - self.resource_info: List = [x for x in resource_data_file.read().splitlines() if not x.startswith('#')] - resource_data_file.close() + self.resource_info: List = resource_data_file.read().splitlines(); resource_data_file.close() self.resource_dict: Dict[str, List[str]] = {} self.source_list: Dict[str, str] = {} @@ -91,12 +88,10 @@ def __init__(self, data_path: str, resource_data: Optional[str] = None) -> None: def parses_resource_file(self) -> None: """Verifies that an input file contains data and then outputs a dictionary where each item is a line from the input file. - Returns: source_list: A dictionary, where the key is the type of data and the value is the file path or url. For example: {'chemical-gomf', 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz', 'phenotype': 'http://purl.obolibrary.org/obo/hp.owl'} - Raises: ValueError: If the file does not contain data. ValueError: If there some of the input URLs were improperly formatted. @@ -106,12 +101,10 @@ def parses_resource_file(self) -> None: def downloads_data_from_url(self) -> None: """Downloads each data source from a list and writes the downloaded file to a directory. - Returns: data_files: A dictionary mapping each source identifier to the local location where it was downloaded. For example: {'chemical-gomf', 'resources/edge_data/chemical-gomf_CTD_chem_go_enriched.tsv', 'phenotype': 'resources/ontologies/hp_with_imports.owl'} - Raises: ValueError: If not all of the URLs returned valid data. """ @@ -122,10 +115,8 @@ def downloads_data_from_url(self) -> None: def extracts_edge_metadata(edge) -> Tuple[str, str, str]: """Processes edge data metadata and returns a dictionary where the keys are the edge type and the values are a list containing mapping and filtering information. - Args: edge: A pipe-delimited string containing information about the edge. For example, - Returns: mapping: Identifier mapping information stored as a node and a filepath to perform identifier mapping on (e.g. node1 - './filepath/mapping_data.txt). @@ -136,7 +127,9 @@ def extracts_edge_metadata(edge) -> Tuple[str, str, str]: """ mapping = ['{} ({})'.format(edge.split('|')[0].split('-')[int(x.split(':')[0])], ''.join(x.split(':')[1])) - if x != 'None' else 'None' for x in edge.split('|')[-3].strip('\n').split(';')] + if x != 'None' + else 'None' + for x in edge.split('|')[-3].strip('\n').split(';')] filtering = ['None' if x == 'None' else 'data[{}] {}'.format(x.split(';')[0], ' '.join(x.split(';')[1:])) if ('in' in x.split(';')[1] and x != 'None') @@ -152,7 +145,6 @@ def extracts_edge_metadata(edge) -> Tuple[str, str, str]: def _writes_source_metadata_locally(self) -> None: """Writes metadata for each imported data source to a text file. - Returns: None """ @@ -177,7 +169,6 @@ def generates_source_metadata(self) -> None: sources that will be used to map identifiers or filter the data. 3 - Data Information: information on the data including: downloaded url, download date, file size in bytes, and the local file location it was downloaded to - Example: EDGE: chemical-gobp DATA PROCESSING INFO @@ -189,7 +180,6 @@ def generates_source_metadata(self) -> None: - DOWNLOAD_DATE = 01/14/2020 - FILE_SIZE_IN_BYTES = 760612373 - DOWNLOADED_FILE_LOCATION = ./resources/edge_data/chemical-gobp_CTD_chem_go_enriched.tsv - Returns: None. """ @@ -233,12 +223,10 @@ def gets_data_type(self) -> str: def parses_resource_file(self) -> None: """Parses data from a file and outputs a list where each item is a line from the input text file. - Returns: source_list: A dictionary, where the key is the type of data and the value is the file path or url. See - example below: {'chemical-gomf': 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz', + example below: {'chemical-gomf', 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz', 'phenotype': 'http://purl.obolibrary.org/obo/hp.owl'} - Raises: TypeError: If the file does not contain data. ValueError: If there some of the input URLs were improperly formatted. @@ -249,23 +237,20 @@ def parses_resource_file(self) -> None: raise TypeError('ERROR: ' + log_str) else: with open(self.data_path, 'r') as file_name: - self.source_list = {row.strip().split('|')[0]: row.strip().split('|')[1].strip() - for row in file_name.read().splitlines() if not row.startswith('#')} + self.source_list = {row.strip().split(',')[0]: row.strip().split(',')[1].strip() + for row in file_name.read().splitlines()} return None def downloads_data_from_url(self, owltools_location: str = os.path.abspath('./pkt_kg/libs/owltools')) -> None: """Takes a string representing a file path/name to a text file as an argument. The function assumes that each item in the input file list is an URL to an OWL/OBO ontology. - For each URL, the referenced ontology is downloaded, and used as input to an OWLTools command line argument ( https://github.com/owlcollab/owltools/wiki/Extract-Properties-Command), which facilitates the downloading of ontologies that are imported by the primary ontology. The function will save the downloaded ontology + imported ontologies. - Args: owltools_location: A string pointing to the location of the owl tools library. - Returns: data_files: A dictionary mapping each source identifier to the local location where it was downloaded. For example: {'chemical-gomf', 'resources/edge_data/chemical-gomf_CTD_chem_go_enriched.tsv', @@ -311,12 +296,10 @@ def gets_data_type(self) -> str: def parses_resource_file(self) -> None: """Verifies a file contains data and then outputs a list where each item is a line from the input text file. - Returns: source_list: A dictionary, where the key is the type of data and the value is the file path or url. See - example below: {'chemical-gomf': 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz', + example below: {'chemical-gomf', 'http://ctdbase.org/reports/CTD_chem_go_enriched.tsv.gz', 'phenotype': 'http://purl.obolibrary.org/obo/hp.owl'} - Raises: TypeError: If the file does not contain data. """ @@ -326,15 +309,14 @@ def parses_resource_file(self) -> None: raise TypeError('ERROR: ' + log_str) else: with open(self.data_path, 'r') as file_name: - self.source_list = {row.strip().split('|')[0]: row.strip().split('|')[1].strip() - for row in file_name.read().splitlines() if not row.startswith('#')} + self.source_list = {row.strip().split(',')[0]: row.strip().split(',')[1].strip() + for row in file_name.read().splitlines()} return None def downloads_data_from_url(self) -> None: """Takes a string representing a file path/name to a text file as an argument. The function assumes that each item in the input file list is a valid URL. - Returns: data_files: A dictionary mapping each source identifier to the local location where it was downloaded. For example: {'chemical-gomf', 'resources/edge_data/chemical-gomf_CTD_chem_go_enriched.tsv', From 125d243ae9a67b3f0f1840c5237cfa64db6ff279 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 14:21:18 -0400 Subject: [PATCH 099/112] fixed import error --- notebooks/Entity_Search_Examples.ipynb | 39 +++++++++++++++----------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/notebooks/Entity_Search_Examples.ipynb b/notebooks/Entity_Search_Examples.ipynb index d7eefcfa..6ac12d79 100644 --- a/notebooks/Entity_Search_Examples.ipynb +++ b/notebooks/Entity_Search_Examples.ipynb @@ -135,6 +135,26 @@ " data_downloader(url, write_location, file_name)" ] }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'entity_metadata_dict.pkl'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "url.split('/')[-1] if 'entity_metadata_dict.pkl' not in url else re.sub(r'\\?.*', '', url.split('/')[-1])" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -548,25 +568,12 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "FileNotFoundError", - "evalue": "[Errno 2] No such file or directory: '../releases/Columbia_Collaboration/tara_anand/data/entity_metadata_dict.pkl?dl=1'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m<ipython-input-10-5aa907383b41>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# load metadata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mfilepath\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwrite_location\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mdata_urls\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'/'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mmax_bytes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0;36m31\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m;\u001b[0m \u001b[0minput_size\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetsize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m;\u001b[0m \u001b[0mbytes_in\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbytearray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'rb'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mf_in\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minput_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_bytes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/genericpath.py\u001b[0m in \u001b[0;36mgetsize\u001b[0;34m(filename)\u001b[0m\n\u001b[1;32m 48\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mgetsize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[0;34m\"\"\"Return the size of a file, reported by os.stat().\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 50\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mst_size\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 51\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 52\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: '../releases/Columbia_Collaboration/tara_anand/data/entity_metadata_dict.pkl?dl=1'" - ] - } - ], + "outputs": [], "source": [ "# load metadata\n", - "filepath = write_location + data_urls[2].split('/')[-1]\n", + "filepath = write_location + re.sub(r'\\?.*', '', data_urls[2].split('/')[-1])\n", "max_bytes = 2**31 - 1; input_size = os.path.getsize(filepath); bytes_in = bytearray(0)\n", "with open(filepath, 'rb') as f_in:\n", " for _ in range(0, input_size, max_bytes):\n", From 525d574288f109a3aead3025c9535fa7950a5db5 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 14:21:44 -0400 Subject: [PATCH 100/112] addressing typing error --- pkt_kg/utils/data_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkt_kg/utils/data_utils.py b/pkt_kg/utils/data_utils.py index 36bb5866..8afcec3d 100644 --- a/pkt_kg/utils/data_utils.py +++ b/pkt_kg/utils/data_utils.py @@ -46,7 +46,7 @@ import requests import shutil import urllib3 # type: ignore -import yaml +import yaml # type: ignore from contextlib import closing from io import BytesIO From bdd7d89c5f5644bd57217f2837cf60b9a2e28ec1 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 14:22:09 -0400 Subject: [PATCH 101/112] rollback for testing --- pkt_kg/knowledge_graph.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/pkt_kg/knowledge_graph.py b/pkt_kg/knowledge_graph.py index cc5feb9c..fb28c0c5 100644 --- a/pkt_kg/knowledge_graph.py +++ b/pkt_kg/knowledge_graph.py @@ -48,7 +48,6 @@ class KGBuilder(object): build types. The current construction approaches are Instance-based and Subclass-based. The three build types are (1) Full (i.e. runs all build steps in the algorithm); (2) Partial (i.e. runs all of the build steps through adding new edges); and (3) Post-Closure: Runs the remaining build steps over a closed knowledge graph. - Attributes: construction: A string indicating the construction approach (i.e. instance or subclass). node_data: A string ("yes" or "no") indicating whether or not to add node data to the knowledge graph. @@ -56,7 +55,6 @@ class KGBuilder(object): decode_owl: A string containing "yes" or "no" indicating whether owl semantics should be removed. cpus: An integer indicating the number of workers to use. write_location: An optional string passed to specify the primary directory to write to. - Raises: ValueError: If the formatting of kg_version is incorrect (i.e. not "v.#.#.#"). ValueError: If write_location, edge_data does not contain a valid filepath. @@ -143,7 +141,6 @@ def reverse_relation_processor(self) -> None: relation data or relation data identifiers and labels. Examples of each dictionary are provided below: relations_dict: {'RO_0002551': 'has skeleton', 'RO_0002442': 'mutualistically interacts with} inverse_relations_dict: {'RO_0000056': 'RO_0000057', 'RO_0000079': 'RO_0000085'} - Returns: None. """ @@ -165,7 +162,6 @@ def construct_knowledge_graph(self) -> None: Create graph subsets; (4) Process node metadata; (5) Merge ontologies; (6) Add master edge list to merged ontologies; (7) Extract and write node metadata; (8) Decode OWL-encoded classes; and (8) Output knowledge graph files and create edge lists. - Returns: None. """ @@ -180,7 +176,6 @@ def gets_build_type(self) -> str: class EdgeConstructor(object): """Inner class object used to facilitate ray parallelization. - Attributes: construction: A string indicating the construction approach (i.e. instance or subclass). edge_data: A nested dictionary keyed by edge type that contains all information needed to construct an edge. @@ -224,13 +219,10 @@ def error_dict_getter(self) -> Dict: def verifies_object_property(self, object_property: URIRef) -> None: """Adds an object property to a knowledge graph. - Args: object_property: A string containing an obo ontology object property. - Returns: None. - Raises: TypeError: If the object_property is not type rdflib.term.URIRef """ @@ -247,13 +239,11 @@ def verifies_object_property(self, object_property: URIRef) -> None: def checks_classes(self, edge_info) -> bool: """Determines whether or not an edge is safe to add to the knowledge graph by making sure that any ontology class nodes are also present in the current list of classes from the merged ontologies graph. - Args: edge_info: A dict of information needed to add edge to graph, for example: {'n1': 'class', 'n2': 'class','rel': 'RO_0002606', 'inv_rel': 'RO_0002615', 'uri': ['https://www.ncbi.nlm.nih.gov/gene/', 'http://purl.obolibrary.org/obo/'], 'edges': ['CHEBI_81395', 'DOID_12858']} - Returns: True - if the class node is already in the graph or nodes are both non-class entities. False - if the edge contains at least 1 ontology class that is not present in the graph. @@ -269,11 +259,9 @@ def checks_relations(self, relation: str, edge_list: Union[List, Set]) -> Option """Determines whether or not an inverse relation should be created and added to the graph and verifies that a relation and its inverse (if it exists) are both an existing owl:ObjectProperty in the graph. - Args: relation: A string that contains the relation assigned to edge in resource_info.txt (e.g. 'RO_0000056'). edge_list: A list or set of knowledge graph edges. For example: {["8837", "4283"], ["8837", "839"]} - Returns: A string containing an ontology identifier (e.g. "RO_0000056) or None. Value depends on: - inverse relation, if the stored relation has an inverse relation @@ -294,12 +282,10 @@ def checks_relations(self, relation: str, edge_list: Union[List, Set]) -> Option @staticmethod def gets_edge_statistics(edge_type: str, results: Set, entity_info: List) -> str: """Calculates the number of nodes and edges involved in constructing an edge type. - Args: edge_type: A string point to a specific edge type (e.g. 'chemical-disease). results: A set of tuples representing the complete set of triples from the construction process. entity_info: 3 items: 1-2 are sets of node tuples and 3 is the total count of non-OWL edges. - Returns: formatted_str: A string containing edge statistics. """ @@ -314,10 +300,8 @@ def gets_edge_statistics(edge_type: str, results: Set, entity_info: List) -> str def creates_new_edges(self, edge_type: str) -> Graph: """Takes a dictionary of information needed to construct and edge creates the associated triples. - Args: edge_type: A list of strings representing the types of edges to build. - Returns: graph: An RDFLib Graph object. """ @@ -362,10 +346,8 @@ def construct_knowledge_graph(self) -> None: knowledge graph and intends to run a reasoner over it. The partial build includes the following steps: (1) Process relation/inverse relations; (2) Merge ontologies; (3) Process node metadata; (4) Create graph subsets; and (5) Add master edge list to merged ontologies. - Returns: None. - Raises: TypeError: If the ontologies directory is empty. """ @@ -444,14 +426,11 @@ def construct_knowledge_graph(self) -> None: """Builds a post-closure knowledge graph. This build is recommended when one has previously performed a "partial" knowledge graph build and then ran a reasoner over it. This build type inputs the closed partially built knowledge graph and completes the build process. - The post-closure build utilizes the following steps: (1) Process relation and inverse relation data; (2) Load closed knowledge graph; (3) Process node metadata; (4) Create graph subsets; (5) Decode OWL-encoded classes; (6) Output knowledge graph files and create edge lists; and (7) Extract and write node metadata. - Returns: None. - Raises: OSError: If closed knowledge graph file does not exist. TypeError: If the closed knowledge graph file is empty. @@ -534,7 +513,6 @@ def construct_knowledge_graph(self) -> None: relations; (2) Merge ontologies; (3) Process node metadata; (4) Create graph subsets; (5) Add master edge list to merged ontologies; (6) Decode OWL-encoded classes; (7) Output knowledge graphs and create edge lists and (8) Extract and write node metadata. - Returns: None. """ From 7c942f8691c8cce20782ba555176bb23222ae323 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 14:24:35 -0400 Subject: [PATCH 102/112] rollback for testing --- pkt_kg/utils/kg_utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkt_kg/utils/kg_utils.py b/pkt_kg/utils/kg_utils.py index 4aa898b4..e44b2857 100644 --- a/pkt_kg/utils/kg_utils.py +++ b/pkt_kg/utils/kg_utils.py @@ -689,6 +689,9 @@ def maps_ids_to_integers(graph: Union[Graph, Set], write_location: str, output_i ids.write(s + '\t' + p + '\t' + o + '\n') output_triples += 1 ints.close(), ids.close() + + # TODO: add an edge identifier and make sure that the output is zipped. + # CHECK - verify we get the number of edges that we would expect to get if graph_len != output_triples: raise ValueError('ERROR: The number of triples is incorrect!') else: From 32ab2d6ab58079b3cf18c36d21230662ff852a4a Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 15:08:04 -0400 Subject: [PATCH 103/112] rollback --- pkt_kg/metadata.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkt_kg/metadata.py b/pkt_kg/metadata.py index b1c106df..1522ca05 100644 --- a/pkt_kg/metadata.py +++ b/pkt_kg/metadata.py @@ -128,15 +128,11 @@ def extract_metadata(self, graph: Graph) -> None: descriptions = [x for x in graph.triples((i, obo.IAO_0000115, None)) if '@' not in n3(x[2]) or '@en' in n3(x[2])] synonyms = [x for x in graph.triples((i, None, None)) if 'synonym' in str(x[1]).lower()] - dbxrefs = [x for x in graph.triples((i, None, None)) - if 'hasdbxref' in str(x[1]).lower() or 'exactmatch' in str(x[1]).lower()] if len(labels) != 0: temp_dict[str(i)] = { 'Label': str(labels[0][2]) if len(labels) > 0 else None, 'Description': str(descriptions[0][2]) if len(descriptions) > 0 else None, - 'Synonym': '|'.join([str(c[2]) for c in synonyms]) if len(synonyms) > 0 else None, - 'DbXref': '|'.join(['{}:{}'.format(str(c[1]), str(c[2])) for c in dbxrefs]) - if len(dbxrefs) > 0 else None + 'Synonym': '|'.join([str(c[2]) for c in synonyms]) if len(synonyms) > 0 else None } self.node_dict[key] = {**self.node_dict[key], **temp_dict} From 4b34596da36125e543df77ba257170e503b0f1bf Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 16:29:23 -0400 Subject: [PATCH 104/112] resolving typing errors --- pkt_kg/knowledge_graph.py | 15 +++++---- pkt_kg/utils/kg_utils.py | 67 +-------------------------------------- 2 files changed, 10 insertions(+), 72 deletions(-) diff --git a/pkt_kg/knowledge_graph.py b/pkt_kg/knowledge_graph.py index fb28c0c5..82a6cb22 100644 --- a/pkt_kg/knowledge_graph.py +++ b/pkt_kg/knowledge_graph.py @@ -397,10 +397,11 @@ def construct_knowledge_graph(self) -> None: actors = [ray.remote(self.EdgeConstructor).remote(args) for _ in range(self.cpus)] # type: ignore for i in range(0, len(edges)): [actors[i].creates_new_edges.remote(j) for j in edges[i]] # type: ignore # extract results, aggregate actor dictionaries into single dictionary, and write data to json file - _ = ray.wait([x.graph_getter.remote() for x in actors], num_returns=len(actors)) - graph_res = ray.get([x.graph_getter.remote() for x in actors]) + _ = ray.wait([x.graph_getter.remote() for x in actors], num_returns=len(actors)) # type: ignore + graph_res = ray.get([x.graph_getter.remote() for x in actors]) # type: ignore graphs = [self.graph] + [x[0] for x in graph_res] # ; clean_graphs = [x[1] for x in graph_res] - error_dicts = dict(ChainMap(*ray.get([x.error_dict_getter.remote() for x in actors]))); del actors + error_dicts = dict(ChainMap(*ray.get([x.error_dict_getter.remote() for x in actors]))) # type: ignore + del actors if len(error_dicts.keys()) > 0: # output error logs log_file = glob.glob(self.res_dir + '/construction*')[0] + '/subclass_map_log.json' logger.info('See log: {}'.format(log_file)); outputs_dictionary_data(error_dicts, log_file) @@ -560,9 +561,11 @@ def construct_knowledge_graph(self) -> None: edges = sublist_creator({k: len(v['edge_list']) for k, v in self.edge_dict.items()}, self.cpus) actors = [ray.remote(self.EdgeConstructor).remote(args) for _ in range(self.cpus)] # type: ignore for i in range(0, len(edges)): [actors[i].creates_new_edges.remote(j) for j in edges[i]] # type: ignore - _ = ray.wait([x.graph_getter.remote() for x in actors], num_returns=len(actors)) - res = ray.get([x.graph_getter.remote() for x in actors]); g1 = [x[0] for x in res]; g2 = [x[1] for x in res] - error_dicts = dict(ChainMap(*ray.get([x.error_dict_getter.remote() for x in actors]))); del actors + _ = ray.wait([x.graph_getter.remote() for x in actors], num_returns=len(actors)) # type: ignore + res = ray.get([x.graph_getter.remote() for x in actors]) # type: ignore + g1 = [x[0] for x in res]; g2 = [x[1] for x in res] + error_dicts = dict(ChainMap(*ray.get([x.error_dict_getter.remote() for x in actors]))) # type: ignore + del actors if len(error_dicts.keys()) > 0: # output error logs log_file = glob.glob(self.res_dir + '/construction*')[0] + '/subclass_map_log.json' logger.info('See log: {}'.format(log_file)); outputs_dictionary_data(error_dicts, log_file) diff --git a/pkt_kg/utils/kg_utils.py b/pkt_kg/utils/kg_utils.py index e44b2857..7464ea4c 100644 --- a/pkt_kg/utils/kg_utils.py +++ b/pkt_kg/utils/kg_utils.py @@ -3,7 +3,6 @@ """ Knowledge Graph Utility Functions. - Interacts with OWL Tools API * gets_ontology_classes * gets_ontology_statistics @@ -13,7 +12,6 @@ * gets_ontology_definitions * merges_ontologies * ontology_file_formatter - Interacts with Knowledge Graphs * adds_edges_to_graph * remove_edges_from_graph @@ -26,12 +24,10 @@ * removes_namespace_from_bnodes * updates_pkt_namespace_identifiers * splits_knowledge_graph - Writes Triple Lists * maps_ids_to_integers * n3 * appends_to_existing_file - File Type Conversion * convert_to_networkx """ @@ -65,13 +61,10 @@ def gets_ontology_classes(graph: Graph) -> Set: """Queries a knowledge graph and returns a list of all owl:Class objects (excluding BNodes) in the graph. - Args: graph: An rdflib Graph object. - Returns: class_list: A list of all of the classes in the graph. - Raises: ValueError: If the query returns zero nodes with type owl:ObjectProperty. """ @@ -83,10 +76,8 @@ def gets_ontology_classes(graph: Graph) -> Set: def gets_ontology_definitions(graph: Graph) -> Dict: """Queries a knowledge graph and returns a list of all object definitions (obo:IAO_0000115) in the graph. - Args: graph: An rdflib Graph object. - Returns: obj_defs: A dictionary where keys are object URiRefs and values are Literal object definitions. For example: {rdflib.term.URIRef('http://purl.obolibrary.org/obo/OBI_0001648'): @@ -103,10 +94,8 @@ def gets_ontology_definitions(graph: Graph) -> Dict: def gets_deprecated_ontology_classes(graph: Graph) -> Set: """Queries a knowledge graph and returns a list of all deprecated owl:Class objects in the graph. - Args: graph: An rdflib Graph object. - Returns: class_list: A list of all of the deprecated OWL classes in the graph. """ @@ -118,13 +107,10 @@ def gets_deprecated_ontology_classes(graph: Graph) -> Set: def gets_object_properties(graph: Graph) -> Set: """Queries a knowledge graph and returns a list of all owl:ObjectProperty objects in the graph. - Args: graph: An rdflib Graph object. - Returns: object_property_list: A list of all of the object properties in the graph. - Raises: ValueError: If the query returns zero nodes with type owl:ObjectProperty. """ @@ -137,10 +123,8 @@ def gets_object_properties(graph: Graph) -> Set: def gets_ontology_class_synonyms(graph: Graph) -> Tuple: """Queries a knowledge graph and returns a tuple of dictionaries. The first dictionary contains all owl:Class objects and their synonyms in the graph. The second dictionary contains the synonyms and their OWL synonym types. - Args: graph: An rdflib Graph object. - Returns: A tuple of dictionaries: synonyms: A dictionary where keys are string synonyms and values are ontology URIs. For example: @@ -165,12 +149,9 @@ def gets_ontology_class_dbxrefs(graph: Graph) -> Tuple: cross references (dbxref). Function also includes exact matches. A tuple of dictionaries: (1) contains dbxref and exact matches (URIs and labels); and (2) contains dbxref/exactmatch uris and a string indicating the type (i.e. dbxref or exact match). - Assumption: That none of the hasdbxref ids overlap with any of the exactmatch ids. - Args: graph: An rdflib Graph object. - Returns: dbxref: A dictionary where keys are dbxref strings and values are ontology URIs. dbxref_type: A dict where keys are dbxref/exact uris; values are str indicating if the uri is dbxref or exact. @@ -196,14 +177,11 @@ def gets_ontology_class_dbxrefs(graph: Graph) -> Tuple: def gets_ontology_statistics(file_location: str, owltools_location: str = './pkt_kg/libs/owltools') -> str: """Uses the OWL Tools API to generate summary statistics (i.e. counts of axioms, classes, object properties, and individuals). - Args: file_location: A string that contains the file path and name of an ontology. owltools_location: A string pointing to the location of the owl tools library. - Returns: stats: A formatted string containing descriptive statistics. - Raises: TypeError: If the file_location is not type str. OSError: If file_location points to a non-existent file. @@ -227,13 +205,11 @@ def merges_ontologies(onts: List[str], loc: str, merged: str, """Using the OWLTools API, each ontology listed in in the ontologies attribute is recursively merged with into a master merged ontology file and saved locally to the provided file path via the merged_ontology attribute. The function assumes that the file is written to the directory specified by the write_location attribute. - Args: onts: A list of ontology file paths. loc: A string pointing to a local directory for writing data. merged: A string pointing to the location of the merged ontology file. owltools: A string pointing to the location of the owl tools library. - Returns: None. """ @@ -253,15 +229,12 @@ def merges_ontologies(onts: List[str], loc: str, merged: str, def ontology_file_formatter(loc: str, full_kg: str, owltools: str = os.path.abspath('./pkt_kg/libs/owltools')) -> None: """Reformat an .owl file to be consistent with the formatting used by the OWL API. To do this, an ontology referenced by graph_location is read in and output to the same location via the OWLTools API. - Args: loc: A string pointing to a local directory for writing data. full_kg: A string containing the subdirectory and name of the the knowledge graph file. owltools: A string pointing to the location of the owl tools library. - Returns: None. - Raises: TypeError: If something other than an .owl file is passed to function. IOError: If the graph_location file is empty. @@ -282,12 +255,10 @@ def ontology_file_formatter(loc: str, full_kg: str, owltools: str = os.path.absp def adds_edges_to_graph(graph: Graph, edge_list: Union[List, Set], progress_bar: bool = True) -> Graph: """Takes a set or list of tuples representing new triples and adds them to a knowledge graph. - Args: graph: An RDFLib Graph object. edge_list: A list or set of tuples, where each tuple contains a triple. progress_bar: A boolean indicating whether or not the progress bar should be used. - Returns: graph: An updated RDFLib graph. """ @@ -301,11 +272,9 @@ def adds_edges_to_graph(graph: Graph, edge_list: Union[List, Set], progress_bar: def remove_edges_from_graph(graph: Graph, edge_list: Union[List, Set]) -> Graph: """Takes a tuple of tuples and removes them from a knowledge graph. - Args: graph: An RDFLib Graph object. edge_list: A list or set of tuples, where each tuple contains a triple. - Returns: graph: An updated RDFLib graph. """ @@ -319,12 +288,10 @@ def remove_edges_from_graph(graph: Graph, edge_list: Union[List, Set]) -> Graph: def updates_graph_namespace(entity_namespace: str, graph: Graph, node: str) -> Graph: """Adds a triple to a graph specifying a node's namespace. This is only used for non-ontology entities. - Args: entity_namespace: A string containing an entity namespace (i.e. "pathway", "gene"). graph: An RDFLib Graph object. node: A string containing the URI for a node in the graph. - Returns: graph: An RDFLib Graph object. """ @@ -337,13 +304,11 @@ def updates_graph_namespace(entity_namespace: str, graph: Graph, node: str) -> G def finds_node_type(edge_info: Dict) -> Dict: """Takes a dictionary of edge information and parses the data type for each node in the edge. Returns either None or a string containing a particular node from the edge. - Args: edge_info: A dict of information needed to add edge to graph, for example: {'n1': 'subclass', 'n2': 'class','relation': 'RO_0003302', 'url': ['https://www.ncbi.nlm.nih.gov/gene/', 'http://purl.obolibrary.org/obo/'], 'edges': ['2', 'DOID_0110035']} - Returns: A dictionary with 4 keys representing node type (i.e. "cls1", "cls2", "ent1", and "ent2") and values are strings containing a concatenation of the uri and the node. An example of a class-class edge is shown below: @@ -372,13 +337,11 @@ def finds_node_type(edge_info: Dict) -> Dict: def gets_entity_ancestors(graph: Graph, uris: List[Union[URIRef, str]], rel: Union[URIRef, str] = RDFS.subClassOf, cls_lst: Optional[List] = None) -> List: """A method that recursively searches an ontology hierarchy to pull all ancestor concepts for an input entity. - Args: graph: An RDFLib graph object assumed to contain ontology data. uris: A list of at least one ontology RDFLib URIRef object or string. rel: A string or RDFLib URI object containing a predicate. cls_lst: A list of URIs representing the ancestor classes found for the input class_uris. - Returns: An ordered (desc; root to leaf) list of ontology objects containing the input uris ancestor hierarchy. Example: input: [URIRef('http://purl.obolibrary.org/NCBITaxon_11157')] @@ -403,10 +366,8 @@ def connected_components(graph: Union[Graph, Set]) -> List: containing the nodes for a given component. This method works by first converting the RDFLib graph into a NetworkX multi-directed graph, which is converted to a undirected graph prior to calculating the connected components. - Args: graph: An RDFLib Graph object. - Returns: components: A list of the nodes in each component detected in the graph. """ @@ -422,10 +383,8 @@ def connected_components(graph: Union[Graph, Set]) -> List: def removes_self_loops(graph: Graph) -> List: """Method iterates over a graph and identifies all triples that contain self-loops. The method returns a list of all self-loops. - Args: graph: An RDFLib Graph object. - Returns: self_loops: A list of triples containing self-loops that need to be removed. """ @@ -442,10 +401,8 @@ def derives_graph_statistics(graph: Union[Graph, Set, nx.MultiDiGraph]) -> str: converting each node to a string before deriving our counts. This is purposeful as the number of unique nodes is altered when you it converted to a string. For example, in the HPO when honoring the RDF type of each node there are 406,717 unique nodes versus 406,331 unique nodes when ignoring the RDF type of each node. - Args: graph: An RDFLib graph object or a networkx.MultiDiGraph. - Returns: stats: A formatted string containing descriptive statistics. """ @@ -486,11 +443,9 @@ def derives_graph_statistics(graph: Union[Graph, Set, nx.MultiDiGraph]) -> str: def adds_namespace_to_bnodes(graph: Graph, ns: Union[str, Namespace] = pkt_bnode) -> Graph: """Method adds a namespace to all anonymous (RDFLib Term type BNode). - Args: graph: An RDFLib Graph object. ns: A string or RDFLib Namespace object (default='https://github.com/callahantiff/PheKnowLator/pkt/bnode/') - Returns: updated_graph: An RDFLib Graph object with updated BNodes. """ @@ -519,12 +474,10 @@ def adds_namespace_to_bnodes(graph: Graph, ns: Union[str, Namespace] = pkt_bnode def removes_namespace_from_bnodes(graph: Graph, ns: Union[str, Namespace] = pkt_bnode, verbose: bool = True) -> Graph: """Methods removes namespace from nodes originally assumed to be RDFLib BNodes. This method acts to reverse the pkt_kg.utils.adds_namespace_to_bnodes method. - Args: graph: An RDFLib Graph object. ns: A string or RDFLib Namespace object (default='https://github.com/callahantiff/PheKnowLator/pkt/bnode/') verbose: A bool flag used to indicate whether or not to print method function (default=False). - Returns: updated_graph: An RDFLib Graph object with bnode namespaces removed. """ @@ -554,15 +507,12 @@ def updates_pkt_namespace_identifiers(graph: Union[Graph, Set], const: str, verb subclass-based construction approaches and converts pkt-namespaced BNodes back to the original ontology class identifier. A new edge for each triple, containing an instance of a class is updated with the original ontology identifier, is added to the graph. - Assumptions: (1) all instances/classes of a BNode identifier contain the pkt namespace and (2) all relations used when adding new edges to a graph are part of the OBO namespace. - Args: graph: An RDFLib Graph object containing pkt-namespacing. const: A string containing the type of construction approach used to build the knowledge graph. verbose: A bool flag used to indicate whether or not to print method function (default=False). - Returns: graph: An RDFLib Graph object or set of RDFLib triples updated to remove bnode namespacing. """ @@ -600,15 +550,12 @@ def splits_knowledge_graph(graph: Graph, graph_output: bool = False) -> Tuple[Gr """Method takes an input RDFLib Graph object and splits it into two new graphs where the first graph contains only those triples needed to maintain a base logical subset and the second contains only annotation assertions. Please note that the code below processes both entities (i.e. owl:Class and owl:ObjectProperties - Source: https://www.w3.org/TR/owl2-syntax/#Annotation_Assertion - Args: graph: An RDFLib Graph object. graph_output: (Bool) if True, the annotation and logic graph are returned as RDFLib Graph objects, if False, the logic_graph is returned as an RDFLib Graph and the annotation subset is returned as a set of triples (default=False). - Returns: logic_graph: An RDFLib Graph object containing only logical axioms. annotation_graph: An RDFLib Graph object or a set of RDFLib triples containing non-logical annotation @@ -656,16 +603,13 @@ def maps_ids_to_integers(graph: Union[Graph, Set], write_location: str, output_i - Identifiers: tab-delimited `.txt` file containing three columns, one for each part of a triple (i.e. subject, predicate, object). Both the subject and object identifiers have not been mapped to integers. - Identifier-Integer Map: JSON file containing a dict where keys are node identifiers and values are integers. - Args: graph: A set of RDFLib Graph object triples or an RDFLib Graph. write_location: A string pointing to a local directory for writing data. output_ints: the name and file path to write out results. output_ints_map: the name and file path to write out results. - Returns: entity_map: A dictionary where keys are integers and values are identifiers. - Raises: ValueError: If the length of the graph is not the same as the number of extracted triples. """ @@ -688,7 +632,7 @@ def maps_ids_to_integers(graph: Union[Graph, Set], write_location: str, output_i s, p, o = s.encode('utf-8').decode(), p.encode('utf-8').decode(), o.encode('utf-8').decode() ids.write(s + '\t' + p + '\t' + o + '\n') output_triples += 1 - ints.close(), ids.close() + # ints.close(), ids.close() # TODO: add an edge identifier and make sure that the output is zipped. @@ -704,12 +648,9 @@ def maps_ids_to_integers(graph: Union[Graph, Set], write_location: str, output_i def n3(node: Union[URIRef, BNode, Literal]) -> str: """Method takes an RDFLib node of type BNode, URIRef, or Literal and serializes it to meet the RDF 1.1 NTriples format. - Src: https://github.com/RDFLib/rdflib/blob/c11f7b503b50b7c3cdeec0f36261fa09b0615380/rdflib/plugins/serializers/nt.py - Args: node: An RDFLib - Returns: serialized_node: A string containing the serialized """ @@ -725,9 +666,7 @@ def convert_to_networkx(write_loc: str, filename: str, graph: Union[Graph, Set], key that is the URI identifier and each edge is given a key which is an md5 hash of the triple and a weight of 0.0. An example of the output is shown below. The md5 hash is meant to store a unique key that represents that predicate with respect to the triples it occurs with. - Source: https://networkx.org/documentation/stable/reference/classes/multidigraph.html - Example: Input: (obo.SO_0000288', RDFS.subClassOf', obo.SO_0000287') Output: @@ -735,13 +674,11 @@ def convert_to_networkx(write_loc: str, filename: str, graph: Union[Graph, Set], (RDFS.subClassOf', {'key': 'http://www.w3.org/2000/01/rdf-schema#subClassOf'}), (obo.SO_0000287, {'key': 'http://purl.obolibrary.org/obo/SO_0000287'})] - edge data: [(obo.SO_0000288, obo.SO_0000287', {'predicate_key': '9cbd4826291e7b38eb', 'weight': 0.0})] - Args: write_loc: A string pointing to a local directory for writing data. filename: A string containing the subdirectory and name of the the knowledge graph file. graph: An RDFLib Graph object or set of RDFLib Graph triples. stats: A bool indicating whether or not to derive network statistics after writing networkx file to disk. - Returns: network_stats: A string containing network statistics information. """ @@ -761,12 +698,10 @@ def convert_to_networkx(write_loc: str, filename: str, graph: Union[Graph, Set], def appends_to_existing_file(edges: Union[List, Set, Graph], filepath: str, sep: str = ' ') -> None: """Method adds data to the end of an existing file. Assumes that it is adding data to the end of a n-triples file. - Args: edges: A list or set of tuple, where each tuple is a triple. Or an RDFLib Graph object. filepath: A string specifying a path to an existing file. sep: A string containing a separator e.g. '\t', ',' (default=' '). - Returns: None. """ From b09258807d07c417994253789fce7e239870b8d9 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Fri, 1 Apr 2022 17:12:12 -0400 Subject: [PATCH 105/112] fixing parsing error --- notebooks/Entity_Search_Examples.ipynb | 201 ++----------------------- 1 file changed, 14 insertions(+), 187 deletions(-) diff --git a/notebooks/Entity_Search_Examples.ipynb b/notebooks/Entity_Search_Examples.ipynb index 6ac12d79..4e9e62f4 100644 --- a/notebooks/Entity_Search_Examples.ipynb +++ b/notebooks/Entity_Search_Examples.ipynb @@ -59,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -70,7 +70,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -105,12 +105,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# notebook will create a temporary directory\n", - "# write_location = '../releases/Columbia_Collaboration/tara_anand/data/'\n", "write_location = '../temp_directory/'\n", "if not os.path.exists(write_location):\n", " os.mkdir(write_location)" @@ -118,7 +117,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -135,26 +134,6 @@ " data_downloader(url, write_location, file_name)" ] }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'entity_metadata_dict.pkl'" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "url.split('/')[-1] if 'entity_metadata_dict.pkl' not in url else re.sub(r'\\?.*', '', url.split('/')[-1])" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -355,17 +334,9 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The knowledge graph contains 780753 nodes and 7787308 edges\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# load the knowledge graph\n", "kg = nx.read_gpickle(write_location + data_urls[0].split('/')[-1])\n", @@ -374,7 +345,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -384,116 +355,9 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "<div>\n", - "<style scoped>\n", - " .dataframe tbody tr th:only-of-type {\n", - " vertical-align: middle;\n", - " }\n", - "\n", - " .dataframe tbody tr th {\n", - " vertical-align: top;\n", - " }\n", - "\n", - " .dataframe thead th {\n", - " text-align: right;\n", - " }\n", - "</style>\n", - "<table border=\"1\" class=\"dataframe\">\n", - " <thead>\n", - " <tr style=\"text-align: right;\">\n", - " <th></th>\n", - " <th>entity_type</th>\n", - " <th>integer_id</th>\n", - " <th>entity_uri</th>\n", - " <th>label</th>\n", - " <th>description/definition</th>\n", - " <th>synonym</th>\n", - " </tr>\n", - " </thead>\n", - " <tbody>\n", - " <tr>\n", - " <th>0</th>\n", - " <td>NODES</td>\n", - " <td>684158</td>\n", - " <td>https://www.ncbi.nlm.nih.gov/snp/rs864622148</td>\n", - " <td>NM_000051.4(ATM):c.5887G&gt;A (p.Asp1963Asn)</td>\n", - " <td>This variant is a germline single nucleotide v...</td>\n", - " <td>None</td>\n", - " </tr>\n", - " <tr>\n", - " <th>1</th>\n", - " <td>NODES</td>\n", - " <td>668197</td>\n", - " <td>https://uswest.ensembl.org/Homo_sapiens/Transc...</td>\n", - " <td>CUL9-211</td>\n", - " <td>Transcript CUL9-211 is classified as type 'non...</td>\n", - " <td>None</td>\n", - " </tr>\n", - " <tr>\n", - " <th>2</th>\n", - " <td>NODES</td>\n", - " <td>769680</td>\n", - " <td>http://purl.obolibrary.org/obo/CHEBI_116891</td>\n", - " <td>3-(3-methylphenyl)-2-sulfanylidene-1H-benzofur...</td>\n", - " <td>None</td>\n", - " <td>None</td>\n", - " </tr>\n", - " <tr>\n", - " <th>3</th>\n", - " <td>NODES</td>\n", - " <td>381659</td>\n", - " <td>https://www.ncbi.nlm.nih.gov/snp/rs61741688</td>\n", - " <td>NM_001272071.2(AP1S2):c.288T&gt;C (p.Ser96=)</td>\n", - " <td>This variant is a germline single nucleotide v...</td>\n", - " <td>None</td>\n", - " </tr>\n", - " <tr>\n", - " <th>4</th>\n", - " <td>NODES</td>\n", - " <td>720533</td>\n", - " <td>http://purl.obolibrary.org/obo/PR_Q9Y2I7-3</td>\n", - " <td>1-phosphatidylinositol 3-phosphate 5-kinase is...</td>\n", - " <td>A 1-phosphatidylinositol 3-phosphate 5-kinase ...</td>\n", - " <td>hPIKFYVE/iso:h3</td>\n", - " </tr>\n", - " </tbody>\n", - "</table>\n", - "</div>" - ], - "text/plain": [ - " entity_type integer_id entity_uri \\\n", - "0 NODES 684158 https://www.ncbi.nlm.nih.gov/snp/rs864622148 \n", - "1 NODES 668197 https://uswest.ensembl.org/Homo_sapiens/Transc... \n", - "2 NODES 769680 http://purl.obolibrary.org/obo/CHEBI_116891 \n", - "3 NODES 381659 https://www.ncbi.nlm.nih.gov/snp/rs61741688 \n", - "4 NODES 720533 http://purl.obolibrary.org/obo/PR_Q9Y2I7-3 \n", - "\n", - " label \\\n", - "0 NM_000051.4(ATM):c.5887G>A (p.Asp1963Asn) \n", - "1 CUL9-211 \n", - "2 3-(3-methylphenyl)-2-sulfanylidene-1H-benzofur... \n", - "3 NM_001272071.2(AP1S2):c.288T>C (p.Ser96=) \n", - "4 1-phosphatidylinositol 3-phosphate 5-kinase is... \n", - "\n", - " description/definition synonym \n", - "0 This variant is a germline single nucleotide v... None \n", - "1 Transcript CUL9-211 is classified as type 'non... None \n", - "2 None None \n", - "3 This variant is a germline single nucleotide v... None \n", - "4 A 1-phosphatidylinositol 3-phosphate 5-kinase ... hPIKFYVE/iso:h3 " - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# read in node metadata\n", "node_data = pd.read_csv(write_location + data_urls[1].split('/')[-1], header=0, sep=r\"\\t\", encoding=\"utf8\", engine='python', quoting=3)\n", @@ -505,46 +369,9 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c94990af9fe146f7ae8e5ea649c19504", - "version_major": 2, - "version_minor": 0 - }, - "text/html": [ - "<p>Failed to display Jupyter Widget of type <code>HBox</code>.</p>\n", - "<p>\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the <a href=\"https://ipywidgets.readthedocs.io/en/stable/user_install.html\">Jupyter\n", - " Widgets Documentation</a> for setup instructions.\n", - "</p>\n", - "<p>\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or <a href=\"https://nbviewer.jupyter.org/\">NBViewer</a>),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "</p>\n" - ], - "text/plain": [ - "HBox(children=(FloatProgress(value=0.0, max=781049.0), HTML(value='')))" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# remove angle brackets\n", "node_data['entity_uri'] = node_data['entity_uri'].str.strip('<>')\n", From ff1e693420ffe57afd4eb79fcd16bd19bbc7a9cf Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 4 Apr 2022 09:20:13 -0400 Subject: [PATCH 106/112] adding footnote --- notebooks/Data_Preparation.ipynb | 6 +++- notebooks/OWLNETS_Example_Application.ipynb | 26 +++++++++++++++ notebooks/Ontology_Cleaning.ipynb | 6 +++- notebooks/RDF_Graph_Processing_Example.ipynb | 33 ++++++++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/notebooks/Data_Preparation.ipynb b/notebooks/Data_Preparation.ipynb index 75557f65..212a75cd 100644 --- a/notebooks/Data_Preparation.ipynb +++ b/notebooks/Data_Preparation.ipynb @@ -7751,6 +7751,8 @@ "***\n", "***\n", "\n", + "This Notebook is part of the [**PheKnowLator Ecosystem**](https://zenodo.org/communities/pheknowlator-ecosystem/edit/)\n", + "\n", "```\n", "@misc{callahan_tj_2019_3401437,\n", " author = {Callahan, TJ},\n", @@ -7760,7 +7762,9 @@ " doi = {10.5281/zenodo.3401437},\n", " url = {https://doi.org/10.5281/zenodo.3401437}\n", "}\n", - "```" + "```\n", + "\n", + "***" ] } ], diff --git a/notebooks/OWLNETS_Example_Application.ipynb b/notebooks/OWLNETS_Example_Application.ipynb index dd7443f2..34a6bb2b 100644 --- a/notebooks/OWLNETS_Example_Application.ipynb +++ b/notebooks/OWLNETS_Example_Application.ipynb @@ -818,6 +818,32 @@ " definitions = entity_metadata['relations'][x]['definitions']\n", " out.write(x + '\\t' + namespace + '\\t' + labels + '\\t' + definitions + '\\n')" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "<br>\n", + "\n", + "***\n", + "***\n", + "\n", + "This Notebook is part of the [**PheKnowLator Ecosystem**](https://zenodo.org/communities/pheknowlator-ecosystem/edit/)\n", + "\n", + "```\n", + "@misc{callahan_tj_2019_3401437,\n", + " author = {Callahan, TJ},\n", + " title = {PheKnowLator},\n", + " month = mar,\n", + " year = 2019,\n", + " doi = {10.5281/zenodo.3401437},\n", + " url = {https://doi.org/10.5281/zenodo.3401437}\n", + "}\n", + "```\n", + "\n", + "***" + ] } ], "metadata": { diff --git a/notebooks/Ontology_Cleaning.ipynb b/notebooks/Ontology_Cleaning.ipynb index a93e6739..af3e11aa 100644 --- a/notebooks/Ontology_Cleaning.ipynb +++ b/notebooks/Ontology_Cleaning.ipynb @@ -575,6 +575,8 @@ "***\n", "***\n", "\n", + "This Notebook is part of the [**PheKnowLator Ecosystem**](https://zenodo.org/communities/pheknowlator-ecosystem/edit/)\n", + "\n", "```\n", "@misc{callahan_tj_2019_3401437,\n", " author = {Callahan, TJ},\n", @@ -584,7 +586,9 @@ " doi = {10.5281/zenodo.3401437},\n", " url = {https://doi.org/10.5281/zenodo.3401437}\n", "}\n", - "```" + "```\n", + "\n", + "***" ] } ], diff --git a/notebooks/RDF_Graph_Processing_Example.ipynb b/notebooks/RDF_Graph_Processing_Example.ipynb index ccf94d84..bcb9da2a 100644 --- a/notebooks/RDF_Graph_Processing_Example.ipynb +++ b/notebooks/RDF_Graph_Processing_Example.ipynb @@ -842,6 +842,39 @@ "gene_drug_disease_graph.serialize(write_location + 'pkt_DrugGeneDisease_subgraph.nt', format='nt')\n", "nx.write_gpickle(nx_graph_dgd, write_location + 'pkt_DrugGeneDisease_NetworkxMultiDiGraph.gpickle')" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "<br>\n", + "\n", + "***\n", + "***\n", + "\n", + "This Notebook is part of the [**PheKnowLator Ecosystem**](https://zenodo.org/communities/pheknowlator-ecosystem/edit/)\n", + "\n", + "```\n", + "@misc{callahan_tj_2019_3401437,\n", + " author = {Callahan, TJ},\n", + " title = {PheKnowLator},\n", + " month = mar,\n", + " year = 2019,\n", + " doi = {10.5281/zenodo.3401437},\n", + " url = {https://doi.org/10.5281/zenodo.3401437}\n", + "}\n", + "```\n", + "\n", + "***" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From 21d3bdb7ca09d4b628c8e942b29a47eba0cd117a Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 4 Apr 2022 09:27:14 -0400 Subject: [PATCH 107/112] fixing header --- notebooks/Data_Preparation.ipynb | 6 ++++-- notebooks/OWLNETS_Example_Application.ipynb | 6 ++++-- notebooks/Ontology_Cleaning.ipynb | 6 ++++-- notebooks/RDF_Graph_Processing_Example.ipynb | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/notebooks/Data_Preparation.ipynb b/notebooks/Data_Preparation.ipynb index 212a75cd..aefef06b 100644 --- a/notebooks/Data_Preparation.ipynb +++ b/notebooks/Data_Preparation.ipynb @@ -6,11 +6,13 @@ "collapsed": true }, "source": [ + "<p align=\"center\">\n", + " <img width='325' src=\"https://user-images.githubusercontent.com/8030363/161553611-51a40cf9-e348-4eff-91bd-ab03eac41dd3.png\" />\n", + "</p>\n", + "\n", "***\n", "***\n", "\n", - "<img width='700' src=\"https://user-images.githubusercontent.com/8030363/108961534-b9a66980-7634-11eb-96e2-cc46589dcb8c.png\" style=\"vertical-align:middle\">\n", - "\n", "## Pre-Knowledge Graph Build Data Preparation\n", "***\n", "\n", diff --git a/notebooks/OWLNETS_Example_Application.ipynb b/notebooks/OWLNETS_Example_Application.ipynb index 34a6bb2b..e410e463 100644 --- a/notebooks/OWLNETS_Example_Application.ipynb +++ b/notebooks/OWLNETS_Example_Application.ipynb @@ -6,11 +6,13 @@ "collapsed": true }, "source": [ + "<p align=\"center\">\n", + " <img width='325' src=\"https://user-images.githubusercontent.com/8030363/161553611-51a40cf9-e348-4eff-91bd-ab03eac41dd3.png\" />\n", + "</p>\n", + "\n", "***\n", "***\n", "\n", - "<img width='700' src=\"https://user-images.githubusercontent.com/8030363/108961534-b9a66980-7634-11eb-96e2-cc46589dcb8c.png\" style=\"vertical-align:middle\">\n", - "\n", "## OWL-NETS Application - Example\n", "\n", "***\n", diff --git a/notebooks/Ontology_Cleaning.ipynb b/notebooks/Ontology_Cleaning.ipynb index af3e11aa..c916c3cc 100644 --- a/notebooks/Ontology_Cleaning.ipynb +++ b/notebooks/Ontology_Cleaning.ipynb @@ -4,11 +4,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "<p align=\"center\">\n", + " <img width='325' src=\"https://user-images.githubusercontent.com/8030363/161553611-51a40cf9-e348-4eff-91bd-ab03eac41dd3.png\" />\n", + "</p>\n", + "\n", "***\n", "***\n", "\n", - "<img width='700' src=\"https://user-images.githubusercontent.com/8030363/108961534-b9a66980-7634-11eb-96e2-cc46589dcb8c.png\" style=\"vertical-align:middle\">\n", - "\n", "## Pre-Knowledge Graph Build Ontology Cleaning\n", "***\n", "***\n", diff --git a/notebooks/RDF_Graph_Processing_Example.ipynb b/notebooks/RDF_Graph_Processing_Example.ipynb index bcb9da2a..a5a22905 100644 --- a/notebooks/RDF_Graph_Processing_Example.ipynb +++ b/notebooks/RDF_Graph_Processing_Example.ipynb @@ -4,11 +4,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "<p align=\"center\">\n", + " <img width='325' src=\"https://user-images.githubusercontent.com/8030363/161553611-51a40cf9-e348-4eff-91bd-ab03eac41dd3.png\" />\n", + "</p>\n", + "\n", "***\n", "***\n", "\n", - "<img width='700' src=\"https://user-images.githubusercontent.com/8030363/108961534-b9a66980-7634-11eb-96e2-cc46589dcb8c.png\" style=\"vertical-align:middle\">\n", - "\n", "## Working with RDF Graphs\n", "\n", "***\n", From 1bcf69ee6319be04720f83bd162bac1505a8d1b4 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 4 Apr 2022 09:48:41 -0400 Subject: [PATCH 108/112] bumping numpy --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7d5b6b60..334564cd 100644 --- a/setup.py +++ b/setup.py @@ -74,7 +74,7 @@ def find_version(*file_paths): 'Cython>=0.29.14', 'more-itertools', 'networkx', - 'numpy>=1.18.1', + 'numpy>=1.21.0', 'openpyxl>=3.0.3', 'pandas>=1.0.5', 'psutil', From 484e9b5d43cdfae2f9cde909591db0494ddcbcd6 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 4 Apr 2022 09:55:31 -0400 Subject: [PATCH 109/112] bumping numpy version --- notebooks/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/requirements.txt b/notebooks/requirements.txt index d4e3beb9..26d7a3e1 100644 --- a/notebooks/requirements.txt +++ b/notebooks/requirements.txt @@ -2,7 +2,7 @@ Cython>=0.29.14 ipywidgets>=7.7.0 more-itertools>=8.6.0 networkx>=2.4 -numpy>=1.18.1 +numpy>=1.21.0 openpyxl>=3.0.3 pandas>=1.0.5 psutil>=5.6.3 From 5446b2344d765cd6a61ecde80447a41f7a9125bb Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 4 Apr 2022 11:57:54 -0400 Subject: [PATCH 110/112] adding entity search helper functions --- notebooks/Entity_Search_Examples.ipynb | 451 ++++++++++++++++--------- pkt_kg/utils/__init__.py | 8 +- pkt_kg/utils/kg_utils.py | 121 ++++++- tests/test_kg_utils.py | 44 +++ 4 files changed, 453 insertions(+), 171 deletions(-) diff --git a/notebooks/Entity_Search_Examples.ipynb b/notebooks/Entity_Search_Examples.ipynb index 4e9e62f4..1141a61b 100644 --- a/notebooks/Entity_Search_Examples.ipynb +++ b/notebooks/Entity_Search_Examples.ipynb @@ -6,12 +6,14 @@ "collapsed": true }, "source": [ + "<p align=\"center\">\n", + " <img width='325' src=\"https://user-images.githubusercontent.com/8030363/161553611-51a40cf9-e348-4eff-91bd-ab03eac41dd3.png\" />\n", + "</p>\n", + "\n", "***\n", "***\n", "\n", - "<img width='700' src=\"https://user-images.githubusercontent.com/8030363/108961534-b9a66980-7634-11eb-96e2-cc46589dcb8c.png\" style=\"vertical-align:middle\">\n", - "\n", - "## Knowledge Graph Entity Search Examples\n", + "# Knowledge Graph Entity Search Examples\n", "\n", "***\n", "\n", @@ -22,13 +24,20 @@ " \n", "<br> \n", "\n", - "### Purpose \n", - "The goal of this notebook is to explore different ways to examine relationships between entities in a PheKnowLator knowledge graph.\n", + "## Purpose \n", + "The goal of this notebook is to explore different ways to examine relationships between different types of entities in a PheKnowLator knowledge graph.\n", "\n", - "#### PheKnowLator Knowledge Graph Build \n", - "This notebook was built using a `v3.0.2` OWL-NETS-abstracted subclass-based build with inverse relations, which is publicly available and can be downloaded using the following links: \n", - "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle) \n", - "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt) " + "### Notebook Organization \n", + "- [Set-Up Environment](#set-environment) \n", + "- [Knowledge Graph Data](#kg-data) \n", + "- [Knowledge-based Characterization](#kg-characterization) \n", + " - [Node-Level Characterization](#node-level) \n", + " - [Path-Level Characterization](#path-level) \n", + "\n", + "***\n", + "***\n", + "\n", + "<br>" ] }, { @@ -38,8 +47,9 @@ "<br>\n", "\n", "*** \n", - "## Set-Up Environment \n", + "## Set-Up Environment <a class=\"anchor\" id=\"set-environment\"></a> \n", "*** \n", + "___\n", "\n", "### Dependencies: [pkt_kg](https://pypi.org/project/pkt-kg/), [networkx](https://pypi.org/project/networkx/), [rdflib](https://pypi.org/project/rdflib/)\n", "\n", @@ -63,7 +73,7 @@ "metadata": {}, "outputs": [], "source": [ - "# # if running a local version of pkt_kg, uncomment the code below\n", + "# # if running a local version (i.e., forked from GitHub) of pkt_kg, uncomment the code below\n", "# import sys\n", "# sys.path.append('../')" ] @@ -93,53 +103,12 @@ "obo = Namespace('http://purl.obolibrary.org/obo/')" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Knowledge Graph \n", - "The initial exploration will be performed using a `v3.0.2` OWL-NETS-abstracted subclass-based build with inverse relations, which is publicly available and can be downloaded using the following links: \n", - "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle) \n", - "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt) \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# notebook will create a temporary directory\n", - "write_location = '../temp_directory/'\n", - "if not os.path.exists(write_location):\n", - " os.mkdir(write_location)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# download data to the data directory\n", - "data_urls = [\n", - " 'https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle',\n", - " 'https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt',\n", - " 'https://www.dropbox.com/s/ev0ea6v6fu70fbl/entity_metadata_dict.pkl?dl=1'\n", - "]\n", - "\n", - "for url in data_urls:\n", - " file_name = url.split('/')[-1] if 'entity_metadata_dict.pkl' not in url else re.sub(r'\\?.*', '', url.split('/')[-1])\n", - " if not os.path.exists(write_location + file_name):\n", - " data_downloader(url, write_location, file_name)" - ] - }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Helper Functions \n", - "Create helper functions that are needed to process node data." + "Helper functions used only by this notebook that are needed to process and label knowledge graph node and edge entities." ] }, { @@ -148,77 +117,40 @@ "metadata": { "code_folding": [ 0, - 28, - 54, - 80, - 105 + 18, + 44, + 69 ] }, "outputs": [], "source": [ - "def nx_ancestor_search(kg: nx.multidigraph.MultiDiGraph, nodes: List, prefix: str, anc_list: Optional[List]=None) -> Union[Callable, List]:\n", - " \"\"\"Returns all ancestors nodes reachable through a direct edge. The returned list is ordered by senority.\n", - " \n", - " Args:\n", - " kg: A networkx MultiDiGraph object.\n", - " nodes: A list of RDFLib URIRef objects or None.\n", - " prefix: A string containing an ontology prefix (e..g., MONDO).\n", - " anc_list: A list that is empty or that contains RDFLib URIRef objects.\n", - " \n", - " Returns:\n", - " anc_list: A list of period-delimited strings, where each string represents a path \n", - " \"\"\"\n", - " \n", - " ancestor_list = [] if anc_list is None else anc_list\n", - " \n", - " if len(nodes) == 0: return ancestor_list\n", - " else:\n", - " node = nodes.pop()\n", - " node_list = list(kg.neighbors(node))\n", - " neighborhood = [a for b in [[[i, n] for j in [kg.get_edge_data(*(node, n)).keys()]\n", - " for i in j] for n in node_list] for a in b]\n", - " ancestors = [x[1] for x in neighborhood if (prefix in str(x[1]) and x[0] == RDFS.subClassOf)]\n", - " if len(ancestors) > 0:\n", - " ancestor_list += [[str(x) for x in ancestors]]\n", - " nodes += ancestors\n", - " return nx_ancestor_search(kg, nodes, prefix, ancestor_list)\n", - " \n", - " \n", - "def processes_ancestor_path_list(path_list: List, node_metadata: Dict) -> Dict:\n", - " \"\"\"Processes a nested list of ancestor paths into a single unique list.\n", - " \n", + "def format_path_ancestors(anc_dict: Dict, node_metadata: Dict) -> List:\n", + " \"\"\"Processes a dictionary of node ancestors into a list.\n", + "\n", " Args:\n", - " path_list: A nested list of ontology URLs, where each list represents a set of ancestors.\n", - " node_metadata: A dictionary \n", - " \n", + " anc_dict: A dictionary where keys are ints formatted as strings and values are sets of URL strings for each\n", + " concept that was found at that level. The level is the distance in the hierarchy from the searched node.\n", + " node_metadata: A nested dictionary containing node attributes.\n", + "\n", " Returns:\n", - " ancestors: A nested list where each inner list contains ontology identifier strings\n", + " ancestors: A nested list where each inner list contains ontology identifier strings.\n", " \"\"\"\n", - " \n", - " anc_dict = dict()\n", - " \n", - " for path in path_list:\n", - " for x in path:\n", - " idx = max([i for i, j in enumerate(path_list) if x in j])\n", - " if str(idx) in anc_dict.keys(): anc_dict[str(idx)] |= {x}\n", - " else: anc_dict[str(idx)] = {x}\n", "\n", - " # reorder and format keys\n", - " ancestors = [['{} ({})'.format(node_data_dict[str(x)]['label'], x) for x in anc_dict[str(k)]]\n", + " ancestors = [['{} ({})'.format(node_metadata[str(x)]['label'], x) for x in anc_dict[str(k)]]\n", " for k in sorted([int(x) for x in anc_dict.keys()])]\n", - " \n", + "\n", " return ancestors\n", "\n", "\n", - "def formats_node_information(neighborhood: List, metadata_dict: Dict, verbose: bool=False) -> None:\n", + "def formats_node_information(node: URIRef, neighborhood: List, metadata_dict: Dict, verbose: bool=False) -> None:\n", " \"\"\"Processes neighborhood results.\n", " \n", " Args:\n", + " node: A string containing a node URL.\n", " neighborhood: A nested list of strings, where each string contains a node identifier.\n", - " metadata_dict: node_metadata: A nested dictionary containing node attributes.\n", + " metadata_dict: A nested dictionary containing node attributes.\n", " verbose: A bool indicating whether or not node and edge metadata should be printed.\n", " \n", - " \n", " Returns:\n", " None\n", " \"\"\"\n", @@ -323,15 +255,73 @@ "source": [ "<br>\n", "\n", - "\n", - "## Loading Data\n", "***\n", "\n", + "## Knowledge Graph Data <a class=\"anchor\" id=\"kg-data\"></a>\n", + "***\n", "___\n", "\n", + "This notebook was built using a `v3.0.2` OWL-NETS-abstracted subclass-based build with inverse relations, which is publicly available and can be downloaded using the following links: \n", + "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle) \n", + "- [PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt](https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt) \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Download Data \n", + "***\n", + "\n", + "The knowledge graph data is publicly available and downloaded from the PheKnowLator project's Google Cloud Storage Bucket: https://console.cloud.google.com/storage/browser/pheknowlator/. Data will be downloaded to a temporary directory created in the PheKnowLator root directory (`PheKnowLator/temp_directory`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# notebook will create a temporary directory and will download data to it\n", + "write_location = '../temp_directory/'\n", + "if not os.path.exists(write_location): os.mkdir(write_location)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# download data to the data directory\n", + "data_urls = [\n", + " 'https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NetworkxMultiDiGraph.gpickle',\n", + " 'https://storage.googleapis.com/pheknowlator/current_build/knowledge_graphs/subclass_builds/inverse_relations/owlnets/PheKnowLator_v3.0.2_full_subclass_inverseRelations_OWLNETS_NodeLabels.txt',\n", + " 'https://www.dropbox.com/s/ev0ea6v6fu70fbl/entity_metadata_dict.pkl?dl=1'\n", + "]\n", + "\n", + "for url in data_urls:\n", + " file_name = url.split('/')[-1] if 'entity_metadata_dict.pkl' not in url else re.sub(r'\\?.*', '', url.split('/')[-1])\n", + " if not os.path.exists(write_location + file_name): data_downloader(url, write_location, file_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Loading Data\n", + "***\n", + "\n", "The knowledge graph will be loaded as a `networkx` MultiDiGraph object and the node labels will be read in and converted to a dictionary to enable easy access to node labels and other relevant metadata." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Knowledge Graph" + ] + }, { "cell_type": "code", "execution_count": null, @@ -353,6 +343,13 @@ "undirected_kg = kg.to_undirected()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Load Node Metadata" + ] + }, { "cell_type": "code", "execution_count": null, @@ -361,9 +358,7 @@ "source": [ "# read in node metadata\n", "node_data = pd.read_csv(write_location + data_urls[1].split('/')[-1], header=0, sep=r\"\\t\", encoding=\"utf8\", engine='python', quoting=3)\n", - "# remove angle brackets\n", - "node_data['entity_uri'] = node_data['entity_uri'].str.strip('<>')\n", - "\n", + "node_data['entity_uri'] = node_data['entity_uri'].str.strip('<>') # remove angle brackets\n", "node_data.head()" ] }, @@ -373,9 +368,6 @@ "metadata": {}, "outputs": [], "source": [ - "# remove angle brackets\n", - "node_data['entity_uri'] = node_data['entity_uri'].str.strip('<>')\n", - "\n", "# convert node data to dictionary\n", "node_data_dict = dict()\n", "for idx, row in tqdm(node_data.iterrows(), total=node_data.shape[0]):\n", @@ -390,6 +382,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "#### Load Node and Edge Evidence\n", "This file is temporary while the next release is being formatted." ] }, @@ -399,11 +392,10 @@ "metadata": {}, "outputs": [], "source": [ - "# load metadata\n", "filepath = write_location + re.sub(r'\\?.*', '', data_urls[2].split('/')[-1])\n", "max_bytes = 2**31 - 1; input_size = os.path.getsize(filepath); bytes_in = bytearray(0)\n", "with open(filepath, 'rb') as f_in:\n", - " for _ in range(0, input_size, max_bytes):\n", + " for _ in tqdm(range(0, input_size, max_bytes)):\n", " bytes_in += f_in.read(max_bytes)\n", "metadata_dict = pickle.loads(bytes_in)" ] @@ -414,8 +406,9 @@ "source": [ "<br>\n", "\n", + "***\n", "\n", - "## Knowledge-based Characterization\n", + "## Knowledge-based Characterization <a class=\"anchor\" id=\"kg-characterization\"></a>\n", "***\n", "____\n", "\n", @@ -430,16 +423,21 @@ " - <u>All Shortest Paths</u>: Returns the shortest simple path, if there are multiple paths of the same length then they are all returned.\n", " - <u>All Simple Paths</u>: A simple path is a path with no repeated nodes. These nodes are identified using a modified depth-first search. Given that there are a lot of these, the initial output is limited to a random draw of 10 paths of length 10 from the first 100 derived paths.\n", " \n", - "For all comparisons, the full edge is returned along with all relevant node and edge metadata provided by each data source.\n", + "<br>\n", + "\n", + "**Important.** Output for the node neighborhood and simple and shortest paths are printed twice. The first time (`verbose=False`), there is minimal node and edge evidence printed. The second time (`verbose=True`), node definitions and any available evidence from the source resources used to build the edge are printed. Note that the metadata for the edges in the neighborhood will only include a definition for the nodes that are connected to each primary node of interest.\n", "\n", - " ---" + "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "<br>\n", + "\n", "### Node-Level Characterization <a class=\"anchor\" id=\"node-level\"></a>\n", + "***\n", "\n", "This section characterizes the following concepts:\n", "- [benazepril (`CHEBI_3011`)](#chebi1) \n", @@ -447,9 +445,7 @@ "- [Acute Myocardial Infarction (`MONDO_0004781`)](#mondo1) \n", "- [Myocardial infarction (`HP_0001658`)](#hpo1)\n", "\n", - "*Note*. All output is presented twice for each analysis, the first without any metadata/evidence and the second time, with metadata. This is done to facilitate readability.\n", - "\n", - "____\n" + "*Note*. All output is presented twice for each analysis, the first without any metadata/evidence and the second time, with metadata. This is done to facilitate readability." ] }, { @@ -470,14 +466,14 @@ "cell_type": "code", "execution_count": null, "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [], "source": [ "# examine the node's ancestors\n", "prefix = 'CHEBI'; node = [obo.CHEBI_3011]\n", - "path_list = nx_ancestor_search(kg, node.copy(), prefix)\n", - "chebi3011_ancestors = processes_ancestor_path_list(path_list, node_data_dict)\n", + "chebi3011_anc_dict = processes_ancestor_path_list(nx_ancestor_search(kg, node.copy(), prefix))\n", + "chebi3011_ancestors = format_path_ancestors(chebi3011_anc_dict, node_data_dict)\n", "\n", "# print results -- nodes are ordered by seniority (higher numbers indicate closer to root)\n", "print('Ancestors of {}\\n'.format(node[0]))\n", @@ -510,21 +506,25 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "# print nodes without definitions\n", - "formats_node_information(chebi3011_sorted_neigbors, node_data_dict, verbose=False)" + "formats_node_information(node, chebi3011_sorted_neigbors, node_data_dict, verbose=False)" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "# print nodes with definitions\n", - "formats_node_information(chebi3011_sorted_neigbors, node_data_dict, verbose=True)" + "formats_node_information(node, chebi3011_sorted_neigbors, node_data_dict, verbose=True)" ] }, { @@ -547,14 +547,15 @@ "cell_type": "code", "execution_count": null, "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [], "source": [ "# examine the node's ancestors\n", "prefix = 'CHEBI'; node = [obo.CHEBI_5778]\n", "path_list = nx_ancestor_search(kg, node.copy(), prefix)\n", - "chebi5778_ancestors = processes_ancestor_path_list(path_list, node_data_dict)\n", + "chebi5778_anc_dict = processes_ancestor_path_list(nx_ancestor_search(kg, node.copy(), prefix))\n", + "chebi5778_ancestors = format_path_ancestors(chebi5778_anc_dict, node_data_dict)\n", "\n", "# print results -- nodes are ordered by seniority (higher numbers indicate closer to root)\n", "print('Ancestors of {}\\n'.format(node[0]))\n", @@ -587,21 +588,25 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "# print nodes without definitions\n", - "formats_node_information(chebi5778_sorted_neigbors, node_data_dict, verbose=False)" + "formats_node_information(node, chebi5778_sorted_neigbors, node_data_dict, verbose=False)" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "# print nodes with definitions\n", - "formats_node_information(chebi5778_sorted_neigbors, node_data_dict, verbose=True)" + "formats_node_information(node, chebi5778_sorted_neigbors, node_data_dict, verbose=True)" ] }, { @@ -629,7 +634,8 @@ "# examine the node's ancestors\n", "prefix = 'MONDO'; node = [obo.MONDO_0005068]\n", "path_list = nx_ancestor_search(kg, node.copy(), prefix)\n", - "mondo0005068_ancestors = processes_ancestor_path_list(path_list, node_data_dict)\n", + "mondo0005068_anc_dict = processes_ancestor_path_list(nx_ancestor_search(kg, node.copy(), prefix))\n", + "mondo0005068_ancestors = format_path_ancestors(mondo0005068_anc_dict, node_data_dict)\n", "\n", "# print results -- nodes are ordered by seniority (higher numbers indicate closer to root)\n", "print('Ancestors of {}\\n'.format(node[0]))\n", @@ -662,21 +668,25 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "# print nodes without definitions\n", - "formats_node_information(mondo0005068_sorted_neigbors, node_data_dict, verbose=False)" + "formats_node_information(node, mondo0005068_sorted_neigbors, node_data_dict, verbose=False)" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "# print nodes with definitions\n", - "formats_node_information(mondo0005068_sorted_neigbors, node_data_dict, verbose=True)" + "formats_node_information(node, mondo0005068_sorted_neigbors, node_data_dict, verbose=True)" ] }, { @@ -704,7 +714,8 @@ "# examine the node's ancestors\n", "prefix = 'HP'; node = [obo.HP_0001658]\n", "path_list = nx_ancestor_search(kg, node.copy(), prefix)\n", - "hp0001658_ancestors = processes_ancestor_path_list(path_list, node_data_dict)\n", + "hp0001658_anc_dict = processes_ancestor_path_list(nx_ancestor_search(kg, node.copy(), prefix))\n", + "hp0001658_ancestors = format_path_ancestors(hp0001658_anc_dict, node_data_dict)\n", "\n", "# print results -- nodes are ordered by seniority (higher numbers indicate closer to root)\n", "print('Ancestors of {}\\n'.format(node[0]))\n", @@ -737,21 +748,25 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "# print nodes without definitions\n", - "formats_node_information(hp0001658_sorted_neigbors, node_data_dict, verbose=False)" + "formats_node_information(node, hp0001658_sorted_neigbors, node_data_dict, verbose=False)" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "# print nodes with definitions\n", - "formats_node_information(hp0001658_sorted_neigbors, node_data_dict, verbose=True)" + "formats_node_information(node, hp0001658_sorted_neigbors, node_data_dict, verbose=True)" ] }, { @@ -760,18 +775,17 @@ "source": [ "<br>\n", "\n", - "___\n", - "\n", "### Path-Level Characterization <a class=\"anchor\" id=\"path-level\"></a>\n", "\n", + "***\n", + "\n", "This section characterizes the following concept pairs:\n", "- [benazepril (`CHEBI_3011`) - Myocardial Infarction (`MONDO_0005068`)](#pair1) \n", "- [hydrochlorothiazide (`CHEBI_5778`) - Myocardial Infarction (`MONDO_0005068`)](#pair2) \n", "- [benazepril (`CHEBI_3011`) - Myocardial infarction (`HP_0001658`)](#pair3) \n", "- [hydrochlorothiazide (`CHEBI_5778`) - Myocardial infarction (`HP_0001658`)](#pair4) \n", "\n", - "*Note*. All output is presented twice for each analysis, the first without any metadata/evidence and the second time, with metadata. This is done to facilitate readability.\n", - "___" + "*Note*. All output is presented twice for each analysis, the first without any metadata/evidence and the second time, with metadata. This is done to facilitate readability." ] }, { @@ -798,7 +812,13 @@ "source": [ "# look at all shortest paths between the nodes in pair\n", "shortest_paths = list(nx.all_shortest_paths(kg, obo.CHEBI_3011, obo.MONDO_0005068))\n", - "formats_path_information(kg, shortest_paths, path_type='shortest', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True)" + "formats_path_information(kg=kg,\n", + " paths=shortest_paths,\n", + " path_type='shortest',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=True)" ] }, { @@ -829,7 +849,15 @@ "outputs": [], "source": [ "# print path information -- without definitions and metadata\n", - "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=False, rand=True, sample_size=10)" + "formats_path_information(kg=kg,\n", + " paths=simple_paths,\n", + " path_type='simple',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=False,\n", + " rand=True,\n", + " sample_size=10)" ] }, { @@ -841,7 +869,15 @@ "outputs": [], "source": [ "# print path information -- with definitions and metadata\n", - "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True, rand=True, sample_size=10)" + "formats_path_information(kg=kg,\n", + " paths=simple_paths,\n", + " path_type='simple',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=True,\n", + " rand=True,\n", + " sample_size=10)" ] }, { @@ -870,7 +906,13 @@ "source": [ "# look at all shortest paths between the nodes in pair\n", "shortest_paths = list(nx.all_shortest_paths(kg, obo.CHEBI_5778, obo.MONDO_0005068))\n", - "formats_path_information(kg, shortest_paths, path_type='shortest', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True)" + "formats_path_information(kg=kg,\n", + " paths=shortest_paths,\n", + " path_type='shortest',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=True)" ] }, { @@ -901,7 +943,15 @@ "outputs": [], "source": [ "# print path information -- without definitions and metadata\n", - "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=False, rand=True, sample_size=10)" + "formats_path_information(kg=kg,\n", + " paths=simple_paths,\n", + " path_type='simple',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=False,\n", + " rand=True,\n", + " sample_size=10)" ] }, { @@ -911,7 +961,15 @@ "outputs": [], "source": [ "# print path information -- with definitions and metadata\n", - "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True, rand=True, sample_size=10)" + "formats_path_information(kg=kg,\n", + " paths=simple_paths,\n", + " path_type='simple',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=True,\n", + " rand=True,\n", + " sample_size=10)" ] }, { @@ -938,7 +996,13 @@ "source": [ "# look at all shortest paths between the nodes in pair\n", "shortest_paths = list(nx.all_shortest_paths(kg, obo.CHEBI_3011, obo.HP_0001658))\n", - "formats_path_information(kg, shortest_paths, path_type='shortest', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True)" + "formats_path_information(kg=kg,\n", + " paths=shortest_paths,\n", + " path_type='shortest',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=True)" ] }, { @@ -971,7 +1035,15 @@ "outputs": [], "source": [ "# print path information -- without definitions and metadata\n", - "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=False, rand=True, sample_size=10)" + "formats_path_information(kg=kg,\n", + " paths=simple_paths,\n", + " path_type='simple',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=False,\n", + " rand=True,\n", + " sample_size=10)" ] }, { @@ -983,7 +1055,15 @@ "outputs": [], "source": [ "# print path information -- with definitions and metadata\n", - "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True, rand=True, sample_size=10)" + "formats_path_information(kg=kg,\n", + " paths=simple_paths,\n", + " path_type='simple',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=True,\n", + " rand=True,\n", + " sample_size=10)" ] }, { @@ -1012,7 +1092,13 @@ "source": [ "# look at all shortest paths between the nodes in pair\n", "shortest_paths = list(nx.all_shortest_paths(kg, obo.CHEBI_5778, obo.HP_0001658))\n", - "formats_path_information(kg, shortest_paths, path_type='shortest', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True)" + "formats_path_information(kg=kg,\n", + " paths=shortest_paths,\n", + " path_type='shortest',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict, \n", + " verbose=True)" ] }, { @@ -1045,7 +1131,15 @@ "outputs": [], "source": [ "# print path information -- without definitions and metadata\n", - "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=False, rand=True, sample_size=10)" + "formats_path_information(kg=kg,\n", + " paths=simple_paths,\n", + " path_type='simple',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=False,\n", + " rand=True,\n", + " sample_size=10)" ] }, { @@ -1057,15 +1151,42 @@ "outputs": [], "source": [ "# print path information -- with definitions and metadata\n", - "formats_path_information(kg, simple_paths, path_type='simple', metadata_func=metadata_formatter, metadata_dict=metadata_dict, node_metadata=node_data_dict, verbose=True, rand=True, sample_size=10)" + "formats_path_information(kg=kg,\n", + " paths=simple_paths,\n", + " path_type='simple',\n", + " metadata_func=metadata_formatter,\n", + " metadata_dict=metadata_dict,\n", + " node_metadata=node_data_dict,\n", + " verbose=True,\n", + " rand=True,\n", + " sample_size=10)" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "\n", + "<br>\n", + "\n", + "***\n", + "***\n", + "\n", + "This Notebook is part of the [**PheKnowLator Ecosystem**](https://zenodo.org/communities/pheknowlator-ecosystem/edit/)\n", + "\n", + "```\n", + "@misc{callahan_tj_2019_3401437,\n", + " author = {Callahan, TJ},\n", + " title = {PheKnowLator},\n", + " month = mar,\n", + " year = 2019,\n", + " doi = {10.5281/zenodo.3401437},\n", + " url = {https://doi.org/10.5281/zenodo.3401437}\n", + "}\n", + "```\n", + "\n", + "***" + ] } ], "metadata": { diff --git a/pkt_kg/utils/__init__.py b/pkt_kg/utils/__init__.py index d055e400..36348066 100644 --- a/pkt_kg/utils/__init__.py +++ b/pkt_kg/utils/__init__.py @@ -16,7 +16,7 @@ 'gets_ontology_class_synonyms', 'gets_ontology_classes', 'gets_ontology_definitions', 'gets_ontology_statistics', 'gzipped_ftp_url_download', 'gzipped_url_download', 'load_jsonl', 'maps_ids_to_integers', 'merges_files', 'merges_ontologies', 'metadata_api_mapper', - 'metadata_dictionary_mapper', 'n3', 'obtains_entity_url', 'ontology_file_formatter', - 'outputs_dictionary_data', 'remove_edges_from_graph', 'removes_namespace_from_bnodes', 'removes_self_loops', - 'splits_knowledge_graph', 'sublist_creator', 'updates_graph_namespace', 'updates_pkt_namespace_identifiers', - 'url_download', 'zipped_url_download'] + 'metadata_dictionary_mapper', 'n3', 'nx_ancestor_search', 'obtains_entity_url', 'ontology_file_formatter', + 'outputs_dictionary_data', 'processes_ancestor_path_list', 'remove_edges_from_graph', + 'removes_namespace_from_bnodes', 'removes_self_loops', 'splits_knowledge_graph', 'sublist_creator', + 'updates_graph_namespace', 'updates_pkt_namespace_identifiers', 'url_download', 'zipped_url_download'] diff --git a/pkt_kg/utils/kg_utils.py b/pkt_kg/utils/kg_utils.py index f9c0667c..08ff4156 100644 --- a/pkt_kg/utils/kg_utils.py +++ b/pkt_kg/utils/kg_utils.py @@ -3,6 +3,7 @@ """ Knowledge Graph Utility Functions. + Interacts with OWL Tools API * gets_ontology_classes * gets_ontology_statistics @@ -12,6 +13,7 @@ * gets_ontology_definitions * merges_ontologies * ontology_file_formatter + Interacts with Knowledge Graphs * adds_edges_to_graph * remove_edges_from_graph @@ -24,10 +26,14 @@ * removes_namespace_from_bnodes * updates_pkt_namespace_identifiers * splits_knowledge_graph +* nx_ancestor_search +* processes_ancestor_path_list + Writes Triple Lists * maps_ids_to_integers * n3 * appends_to_existing_file + File Type Conversion * convert_to_networkx """ @@ -44,11 +50,12 @@ from more_itertools import unique_everseen # type: ignore from rdflib import BNode, Graph, Literal, Namespace, URIRef # type: ignore from rdflib.namespace import OWL, RDF, RDFS # type: ignore +# noinspection PyProtectedMember from rdflib.plugins.serializers.nt import _quoteLiteral # type: ignore import subprocess from tqdm import tqdm # type: ignore -from typing import Dict, List, Optional, Set, Tuple, Union +from typing import Callable, Dict, List, Optional, Set, Tuple, Union from pkt_kg.utils import * # set-up environment variables @@ -61,10 +68,13 @@ def gets_ontology_classes(graph: Graph) -> Set: """Queries a knowledge graph and returns a list of all owl:Class objects (excluding BNodes) in the graph. + Args: graph: An rdflib Graph object. + Returns: class_list: A list of all of the classes in the graph. + Raises: ValueError: If the query returns zero nodes with type owl:ObjectProperty. """ @@ -76,8 +86,10 @@ def gets_ontology_classes(graph: Graph) -> Set: def gets_ontology_definitions(graph: Graph) -> Dict: """Queries a knowledge graph and returns a list of all object definitions (obo:IAO_0000115) in the graph. + Args: graph: An rdflib Graph object. + Returns: obj_defs: A dictionary where keys are object URiRefs and values are Literal object definitions. For example: {rdflib.term.URIRef('http://purl.obolibrary.org/obo/OBI_0001648'): @@ -94,8 +106,10 @@ def gets_ontology_definitions(graph: Graph) -> Dict: def gets_deprecated_ontology_classes(graph: Graph) -> Set: """Queries a knowledge graph and returns a list of all deprecated owl:Class objects in the graph. + Args: graph: An rdflib Graph object. + Returns: class_list: A list of all of the deprecated OWL classes in the graph. """ @@ -107,10 +121,13 @@ def gets_deprecated_ontology_classes(graph: Graph) -> Set: def gets_object_properties(graph: Graph) -> Set: """Queries a knowledge graph and returns a list of all owl:ObjectProperty objects in the graph. + Args: graph: An rdflib Graph object. + Returns: object_property_list: A list of all of the object properties in the graph. + Raises: ValueError: If the query returns zero nodes with type owl:ObjectProperty. """ @@ -123,8 +140,10 @@ def gets_object_properties(graph: Graph) -> Set: def gets_ontology_class_synonyms(graph: Graph) -> Tuple: """Queries a knowledge graph and returns a tuple of dictionaries. The first dictionary contains all owl:Class objects and their synonyms in the graph. The second dictionary contains the synonyms and their OWL synonym types. + Args: graph: An rdflib Graph object. + Returns: A tuple of dictionaries: synonyms: A dictionary where keys are string synonyms and values are ontology URIs. For example: @@ -149,9 +168,12 @@ def gets_ontology_class_dbxrefs(graph: Graph) -> Tuple: cross references (dbxref). Function also includes exact matches. A tuple of dictionaries: (1) contains dbxref and exact matches (URIs and labels); and (2) contains dbxref/exactmatch uris and a string indicating the type (i.e. dbxref or exact match). + Assumption: That none of the hasdbxref ids overlap with any of the exactmatch ids. + Args: graph: An rdflib Graph object. + Returns: dbxref: A dictionary where keys are dbxref strings and values are ontology URIs. dbxref_type: A dict where keys are dbxref/exact uris; values are str indicating if the uri is dbxref or exact. @@ -177,11 +199,14 @@ def gets_ontology_class_dbxrefs(graph: Graph) -> Tuple: def gets_ontology_statistics(file_location: str, owltools_location: str = './pkt_kg/libs/owltools') -> str: """Uses the OWL Tools API to generate summary statistics (i.e. counts of axioms, classes, object properties, and individuals). + Args: file_location: A string that contains the file path and name of an ontology. owltools_location: A string pointing to the location of the owl tools library. + Returns: stats: A formatted string containing descriptive statistics. + Raises: TypeError: If the file_location is not type str. OSError: If file_location points to a non-existent file. @@ -205,11 +230,13 @@ def merges_ontologies(onts: List[str], loc: str, merged: str, """Using the OWLTools API, each ontology listed in in the ontologies attribute is recursively merged with into a master merged ontology file and saved locally to the provided file path via the merged_ontology attribute. The function assumes that the file is written to the directory specified by the write_location attribute. + Args: onts: A list of ontology file paths. loc: A string pointing to a local directory for writing data. merged: A string pointing to the location of the merged ontology file. owltools: A string pointing to the location of the owl tools library. + Returns: None. """ @@ -229,12 +256,15 @@ def merges_ontologies(onts: List[str], loc: str, merged: str, def ontology_file_formatter(loc: str, full_kg: str, owltools: str = os.path.abspath('./pkt_kg/libs/owltools')) -> None: """Reformat an .owl file to be consistent with the formatting used by the OWL API. To do this, an ontology referenced by graph_location is read in and output to the same location via the OWLTools API. + Args: loc: A string pointing to a local directory for writing data. full_kg: A string containing the subdirectory and name of the the knowledge graph file. owltools: A string pointing to the location of the owl tools library. + Returns: None. + Raises: TypeError: If something other than an .owl file is passed to function. IOError: If the graph_location file is empty. @@ -255,10 +285,12 @@ def ontology_file_formatter(loc: str, full_kg: str, owltools: str = os.path.absp def adds_edges_to_graph(graph: Graph, edge_list: Union[List, Set], progress_bar: bool = True) -> Graph: """Takes a set or list of tuples representing new triples and adds them to a knowledge graph. + Args: graph: An RDFLib Graph object. edge_list: A list or set of tuples, where each tuple contains a triple. progress_bar: A boolean indicating whether or not the progress bar should be used. + Returns: graph: An updated RDFLib graph. """ @@ -272,9 +304,11 @@ def adds_edges_to_graph(graph: Graph, edge_list: Union[List, Set], progress_bar: def remove_edges_from_graph(graph: Graph, edge_list: Union[List, Set]) -> Graph: """Takes a tuple of tuples and removes them from a knowledge graph. + Args: graph: An RDFLib Graph object. edge_list: A list or set of tuples, where each tuple contains a triple. + Returns: graph: An updated RDFLib graph. """ @@ -288,10 +322,12 @@ def remove_edges_from_graph(graph: Graph, edge_list: Union[List, Set]) -> Graph: def updates_graph_namespace(entity_namespace: str, graph: Graph, node: str) -> Graph: """Adds a triple to a graph specifying a node's namespace. This is only used for non-ontology entities. + Args: entity_namespace: A string containing an entity namespace (i.e. "pathway", "gene"). graph: An RDFLib Graph object. node: A string containing the URI for a node in the graph. + Returns: graph: An RDFLib Graph object. """ @@ -304,11 +340,13 @@ def updates_graph_namespace(entity_namespace: str, graph: Graph, node: str) -> G def finds_node_type(edge_info: Dict) -> Dict: """Takes a dictionary of edge information and parses the data type for each node in the edge. Returns either None or a string containing a particular node from the edge. + Args: edge_info: A dict of information needed to add edge to graph, for example: {'n1': 'subclass', 'n2': 'class','relation': 'RO_0003302', 'url': ['https://www.ncbi.nlm.nih.gov/gene/', 'http://purl.obolibrary.org/obo/'], 'edges': ['2', 'DOID_0110035']} + Returns: A dictionary with 4 keys representing node type (i.e. "cls1", "cls2", "ent1", and "ent2") and values are strings containing a concatenation of the uri and the node. An example of a class-class edge is shown below: @@ -337,11 +375,13 @@ def finds_node_type(edge_info: Dict) -> Dict: def gets_entity_ancestors(graph: Graph, uris: List[Union[URIRef, str]], rel: Union[URIRef, str] = RDFS.subClassOf, cls_lst: Optional[List] = None) -> List: """A method that recursively searches an ontology hierarchy to pull all ancestor concepts for an input entity. + Args: graph: An RDFLib graph object assumed to contain ontology data. uris: A list of at least one ontology RDFLib URIRef object or string. rel: A string or RDFLib URI object containing a predicate. cls_lst: A list of URIs representing the ancestor classes found for the input class_uris. + Returns: An ordered (desc; root to leaf) list of ontology objects containing the input uris ancestor hierarchy. Example: input: [URIRef('http://purl.obolibrary.org/NCBITaxon_11157')] @@ -366,8 +406,10 @@ def connected_components(graph: Union[Graph, Set]) -> List: containing the nodes for a given component. This method works by first converting the RDFLib graph into a NetworkX multi-directed graph, which is converted to a undirected graph prior to calculating the connected components. + Args: graph: An RDFLib Graph object. + Returns: components: A list of the nodes in each component detected in the graph. """ @@ -383,8 +425,10 @@ def connected_components(graph: Union[Graph, Set]) -> List: def removes_self_loops(graph: Graph) -> List: """Method iterates over a graph and identifies all triples that contain self-loops. The method returns a list of all self-loops. + Args: graph: An RDFLib Graph object. + Returns: self_loops: A list of triples containing self-loops that need to be removed. """ @@ -401,8 +445,10 @@ def derives_graph_statistics(graph: Union[Graph, Set, nx.MultiDiGraph]) -> str: converting each node to a string before deriving our counts. This is purposeful as the number of unique nodes is altered when you it converted to a string. For example, in the HPO when honoring the RDF type of each node there are 406,717 unique nodes versus 406,331 unique nodes when ignoring the RDF type of each node. + Args: graph: An RDFLib graph object or a networkx.MultiDiGraph. + Returns: stats: A formatted string containing descriptive statistics. """ @@ -443,9 +489,11 @@ def derives_graph_statistics(graph: Union[Graph, Set, nx.MultiDiGraph]) -> str: def adds_namespace_to_bnodes(graph: Graph, ns: Union[str, Namespace] = pkt_bnode) -> Graph: """Method adds a namespace to all anonymous (RDFLib Term type BNode). + Args: graph: An RDFLib Graph object. ns: A string or RDFLib Namespace object (default='https://github.com/callahantiff/PheKnowLator/pkt/bnode/') + Returns: updated_graph: An RDFLib Graph object with updated BNodes. """ @@ -474,10 +522,12 @@ def adds_namespace_to_bnodes(graph: Graph, ns: Union[str, Namespace] = pkt_bnode def removes_namespace_from_bnodes(graph: Graph, ns: Union[str, Namespace] = pkt_bnode, verbose: bool = True) -> Graph: """Methods removes namespace from nodes originally assumed to be RDFLib BNodes. This method acts to reverse the pkt_kg.utils.adds_namespace_to_bnodes method. + Args: graph: An RDFLib Graph object. ns: A string or RDFLib Namespace object (default='https://github.com/callahantiff/PheKnowLator/pkt/bnode/') verbose: A bool flag used to indicate whether or not to print method function (default=False). + Returns: updated_graph: An RDFLib Graph object with bnode namespaces removed. """ @@ -507,12 +557,15 @@ def updates_pkt_namespace_identifiers(graph: Union[Graph, Set], const: str, verb subclass-based construction approaches and converts pkt-namespaced BNodes back to the original ontology class identifier. A new edge for each triple, containing an instance of a class is updated with the original ontology identifier, is added to the graph. + Assumptions: (1) all instances/classes of a BNode identifier contain the pkt namespace and (2) all relations used when adding new edges to a graph are part of the OBO namespace. + Args: graph: An RDFLib Graph object containing pkt-namespacing. const: A string containing the type of construction approach used to build the knowledge graph. verbose: A bool flag used to indicate whether or not to print method function (default=False). + Returns: graph: An RDFLib Graph object or set of RDFLib triples updated to remove bnode namespacing. """ @@ -550,12 +603,15 @@ def splits_knowledge_graph(graph: Graph, graph_output: bool = False) -> Tuple[Gr """Method takes an input RDFLib Graph object and splits it into two new graphs where the first graph contains only those triples needed to maintain a base logical subset and the second contains only annotation assertions. Please note that the code below processes both entities (i.e. owl:Class and owl:ObjectProperties + Source: https://www.w3.org/TR/owl2-syntax/#Annotation_Assertion + Args: graph: An RDFLib Graph object. graph_output: (Bool) if True, the annotation and logic graph are returned as RDFLib Graph objects, if False, the logic_graph is returned as an RDFLib Graph and the annotation subset is returned as a set of triples (default=False). + Returns: logic_graph: An RDFLib Graph object containing only logical axioms. annotation_graph: An RDFLib Graph object or a set of RDFLib triples containing non-logical annotation @@ -603,13 +659,16 @@ def maps_ids_to_integers(graph: Union[Graph, Set], write_location: str, output_i - Identifiers: tab-delimited `.txt` file containing three columns, one for each part of a triple (i.e. subject, predicate, object). Both the subject and object identifiers have not been mapped to integers. - Identifier-Integer Map: JSON file containing a dict where keys are node identifiers and values are integers. + Args: graph: A set of RDFLib Graph object triples or an RDFLib Graph. write_location: A string pointing to a local directory for writing data. output_ints: the name and file path to write out results. output_ints_map: the name and file path to write out results. + Returns: entity_map: A dictionary where keys are integers and values are identifiers. + Raises: ValueError: If the length of the graph is not the same as the number of extracted triples. """ @@ -632,7 +691,7 @@ def maps_ids_to_integers(graph: Union[Graph, Set], write_location: str, output_i s, p, o = s.encode('utf-8').decode(), p.encode('utf-8').decode(), o.encode('utf-8').decode() ids.write(s + '\t' + p + '\t' + o + '\n') output_triples += 1 - # ints.close(), ids.close() + # TODO: add an edge identifier and make sure that the output is zipped. # CHECK - verify we get the number of edges that we would expect to get @@ -647,9 +706,12 @@ def maps_ids_to_integers(graph: Union[Graph, Set], write_location: str, output_i def n3(node: Union[URIRef, BNode, Literal]) -> str: """Method takes an RDFLib node of type BNode, URIRef, or Literal and serializes it to meet the RDF 1.1 NTriples format. + Src: https://github.com/RDFLib/rdflib/blob/c11f7b503b50b7c3cdeec0f36261fa09b0615380/rdflib/plugins/serializers/nt.py + Args: node: An RDFLib + Returns: serialized_node: A string containing the serialized """ @@ -665,7 +727,9 @@ def convert_to_networkx(write_loc: str, filename: str, graph: Union[Graph, Set], key that is the URI identifier and each edge is given a key which is an md5 hash of the triple and a weight of 0.0. An example of the output is shown below. The md5 hash is meant to store a unique key that represents that predicate with respect to the triples it occurs with. + Source: https://networkx.org/documentation/stable/reference/classes/multidigraph.html + Example: Input: (obo.SO_0000288', RDFS.subClassOf', obo.SO_0000287') Output: @@ -673,11 +737,13 @@ def convert_to_networkx(write_loc: str, filename: str, graph: Union[Graph, Set], (RDFS.subClassOf', {'key': 'http://www.w3.org/2000/01/rdf-schema#subClassOf'}), (obo.SO_0000287, {'key': 'http://purl.obolibrary.org/obo/SO_0000287'})] - edge data: [(obo.SO_0000288, obo.SO_0000287', {'predicate_key': '9cbd4826291e7b38eb', 'weight': 0.0})] + Args: write_loc: A string pointing to a local directory for writing data. filename: A string containing the subdirectory and name of the the knowledge graph file. graph: An RDFLib Graph object or set of RDFLib Graph triples. stats: A bool indicating whether or not to derive network statistics after writing networkx file to disk. + Returns: network_stats: A string containing network statistics information. """ @@ -697,10 +763,12 @@ def convert_to_networkx(write_loc: str, filename: str, graph: Union[Graph, Set], def appends_to_existing_file(edges: Union[List, Set, Graph], filepath: str, sep: str = ' ') -> None: """Method adds data to the end of an existing file. Assumes that it is adding data to the end of a n-triples file. + Args: edges: A list or set of tuple, where each tuple is a triple. Or an RDFLib Graph object. filepath: A string specifying a path to an existing file. sep: A string containing a separator e.g. '\t', ',' (default=' '). + Returns: None. """ @@ -712,3 +780,52 @@ def appends_to_existing_file(edges: Union[List, Set, Graph], filepath: str, sep: out.close() return None + + +def nx_ancestor_search(kg: nx.multidigraph.MultiDiGraph, nodes: List, prefix: str, anc_list: Optional[List] = None) ->\ + Union[Callable, List]: + """Returns all ancestors nodes reachable through a direct edge. The returned list is ordered by seniority. + + Args: + kg: A networkx MultiDiGraph object. + nodes: A list of RDFLib URIRef objects or None. + prefix: A string containing an ontology prefix (e.g., MONDO). + anc_list: A list that is empty or that contains RDFLib URIRef objects. + + Returns: + anc_list: A list of period-delimited strings, where each string represents a path + """ + + ancestor_list = [] if anc_list is None else anc_list + + if len(nodes) == 0: return ancestor_list + else: + node = nodes.pop(); node_list = list(kg.neighbors(node)) + neighborhood = [a for b in [[[i, n] for j in [kg.get_edge_data(*(node, n)).keys()] + for i in j] for n in node_list] for a in b] + ancestors = [x[1] for x in neighborhood if (prefix in str(x[1]) and x[0] == RDFS.subClassOf)] + if len(ancestors) > 0: + ancestor_list += [[str(x) for x in ancestors]] + nodes += ancestors + return nx_ancestor_search(kg, nodes, prefix, ancestor_list) + + +def processes_ancestor_path_list(path_list: List) -> Dict: + """Processes a nested list of ancestor paths into a dictionary. + + Args: + path_list: A nested list of ontology URLs, where each list represents a set of ancestors. + + Returns: + ancestors: A dictionary where keys are ints formatted as strings and values are sets of URL strings for each + concept that was found at that level. The level is the distance in the hierarchy from the searched node. + """ + + anc_dict: Dict = dict() + for path in path_list: + for x in path: + idx = max([i for i, j in enumerate(path_list) if x in j]) + if str(idx) in anc_dict.keys(): anc_dict[str(idx)] |= {x} + else: anc_dict[str(idx)] = {x} + + return anc_dict diff --git a/tests/test_kg_utils.py b/tests/test_kg_utils.py index 168ad3a6..d17e952f 100644 --- a/tests/test_kg_utils.py +++ b/tests/test_kg_utils.py @@ -775,3 +775,47 @@ def test_updates_pkt_namespace_identifiers_edges2(self): RDFS.subClassOf, URIRef('http://www.ncbi.nlm.nih.gov/gene/4841'))) in result_graph) return None + + def tests_nx_ancestor_search(self): + """Tests the nx_ancestor_search method.""" + + # create test data + graph = Graph().parse(self.dir_loc + '/so_with_imports.owl') + kg = nx.MultiDiGraph() + for s, p, o in graph: + kg.add_node(s, key=n3(s)); kg.add_node(o, key=n3(o)) + kg.add_edge(s, o, **{'key': p, 'weight': 0.0}) + nodes = [obo.SO_0001544] + prefix = 'SO' + + # test method + result_list = nx_ancestor_search(kg, nodes, prefix) + self.assertIsInstance(result_list, List) + self.assertEqual(len(result_list), 5) + self.assertEqual(result_list, [['http://purl.obolibrary.org/obo/SO_0001543'], + ['http://purl.obolibrary.org/obo/SO_0001538'], + ['http://purl.obolibrary.org/obo/SO_0002218'], + ['http://purl.obolibrary.org/obo/SO_0001536'], + ['http://purl.obolibrary.org/obo/SO_0001060']]) + + return None + + def test_processes_ancestor_path_list(self): + """Tests the processes_ancestor_path_list method.""" + + # create test data + path = [['http://purl.obolibrary.org/obo/SO_0001543'], ['http://purl.obolibrary.org/obo/SO_0001538'], + ['http://purl.obolibrary.org/obo/SO_0002218'], ['http://purl.obolibrary.org/obo/SO_0001536'], + ['http://purl.obolibrary.org/obo/SO_0001060']] + + # test method + result = processes_ancestor_path_list(path) + self.assertIsInstance(result, Dict) + self.assertEqual(len(result.keys()), 5) + self.assertEqual(result['0'], {'http://purl.obolibrary.org/obo/SO_0001543'}) + self.assertEqual(result['1'], {'http://purl.obolibrary.org/obo/SO_0001538'}) + self.assertEqual(result['2'], {'http://purl.obolibrary.org/obo/SO_0002218'}) + self.assertEqual(result['3'], {'http://purl.obolibrary.org/obo/SO_0001536'}) + self.assertEqual(result['4'], {'http://purl.obolibrary.org/obo/SO_0001060'}) + + return None From 3815e0872a3322f538913fc29a633c3e7971d88c Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 4 Apr 2022 12:01:19 -0400 Subject: [PATCH 111/112] fixing numpy version error --- notebooks/requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/notebooks/requirements.txt b/notebooks/requirements.txt index 26d7a3e1..7cd27151 100644 --- a/notebooks/requirements.txt +++ b/notebooks/requirements.txt @@ -2,7 +2,7 @@ Cython>=0.29.14 ipywidgets>=7.7.0 more-itertools>=8.6.0 networkx>=2.4 -numpy>=1.21.0 +numpy>=1.19.5 openpyxl>=3.0.3 pandas>=1.0.5 psutil>=5.6.3 diff --git a/setup.py b/setup.py index 334564cd..54663215 100644 --- a/setup.py +++ b/setup.py @@ -74,7 +74,7 @@ def find_version(*file_paths): 'Cython>=0.29.14', 'more-itertools', 'networkx', - 'numpy>=1.21.0', + 'numpy>=1.19.5', 'openpyxl>=3.0.3', 'pandas>=1.0.5', 'psutil', From ca1652717bcc81c24d893af8228080cc4fba1a74 Mon Sep 17 00:00:00 2001 From: callahantiff <callahantiff@gmail.com> Date: Mon, 4 Apr 2022 12:01:56 -0400 Subject: [PATCH 112/112] Update build_requirements.txt --- builds/build_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builds/build_requirements.txt b/builds/build_requirements.txt index e5c1265f..22cb615d 100644 --- a/builds/build_requirements.txt +++ b/builds/build_requirements.txt @@ -6,7 +6,7 @@ google-api-python-client~=1.7.9 google-cloud-storage==1.28.0 lxml>=4.6.5 networkx==2.4 -numpy==1.21.0 +numpy==1.19.5 openpyxl==3.0.3 oauth2client~=4.1.3 Owlready2==0.25