From 5044fce9441c222e52ee16957423107b8b496674 Mon Sep 17 00:00:00 2001 From: yuqiyuqitan Date: Thu, 15 Oct 2020 16:15:04 -0400 Subject: [PATCH 1/5] new name and change the documentation with the new release --- pySingleCellNet/scn_train.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pySingleCellNet/scn_train.py b/pySingleCellNet/scn_train.py index a4d08c2..7c58324 100644 --- a/pySingleCellNet/scn_train.py +++ b/pySingleCellNet/scn_train.py @@ -8,6 +8,16 @@ from .tsp_rf import * def randomize(expDat, num=50): + + """Create random cell profiles from existing data + Args: + expDat (pandas dataframe): Gene Expression matrix + + Returns: + gene expression matrix with added random cell profiles + + """ + temp=expDat.to_numpy() temp=np.array([np.random.choice(x, len(x), replace=False) for x in temp]) temp=temp.T @@ -15,6 +25,16 @@ def randomize(expDat, num=50): return pd.DataFrame(data=temp, columns=expDat.columns).iloc[0:num,:] def sc_trans_rnaseq(aDat,total = 10000 ): + + """Nomarlize and log-transform the raw expression count + Args: + expDat (pandas dataframe): Gene Expression matrix (raw counts) + total (int): total count + dThresh (int): threshold for detection + Returns: + normalized and log-transformed gene expression matrix + """ + sc.pp.normalize_per_cell(aDat, counts_per_cell_after=total) sc.pp.log1p(aDat) sc.pp.scale(aDat, max_value=10) @@ -22,6 +42,19 @@ def sc_trans_rnaseq(aDat,total = 10000 ): return aDat def sc_makeClassifier(expTrain, genes, groups, nRand=70, ntrees=2000, stratify=False): + + """Build random forest classifier + Args: + expTrain (pandas dataframe): Gene Expression matrix (raw counts) + genes (str): gene used for training + groups (str): annotation used for training + nRand (int): number of random cell profiles generated + ntrees (int): number of decision trees used in RandomForestClassifier + stratify (bool): whether to stratify samples for each class + Returns: + RandomForestClassifier + """ + randDat = randomize(expTrain, num=nRand) expT = pd.concat([expTrain, randDat]) allgenes = expT.columns.values From d6fc4a5e477858bfe062522d03c66a638f2db52a Mon Sep 17 00:00:00 2001 From: yuqiyuqitan Date: Thu, 15 Oct 2020 16:36:01 -0400 Subject: [PATCH 2/5] add annotation for scn_train.py --- pySingleCellNet/scn_train.py | 91 +++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/pySingleCellNet/scn_train.py b/pySingleCellNet/scn_train.py index 7c58324..92b98fe 100644 --- a/pySingleCellNet/scn_train.py +++ b/pySingleCellNet/scn_train.py @@ -10,11 +10,16 @@ def randomize(expDat, num=50): """Create random cell profiles from existing data - Args: + Parameteres + ----------- + expDat: expDat (pandas dataframe): Gene Expression matrix + num: int + number of profiles randomly generated Returns: - gene expression matrix with added random cell profiles + -------- + a randomized gene expression matrix (pandas dataframe) with added random cell profiles """ @@ -27,11 +32,15 @@ def randomize(expDat, num=50): def sc_trans_rnaseq(aDat,total = 10000 ): """Nomarlize and log-transform the raw expression count - Args: + Parameteres + ----------- + aDat: expDat (pandas dataframe): Gene Expression matrix (raw counts) - total (int): total count - dThresh (int): threshold for detection + total: int + total count after normalization + Returns: + -------- normalized and log-transformed gene expression matrix """ @@ -44,14 +53,23 @@ def sc_trans_rnaseq(aDat,total = 10000 ): def sc_makeClassifier(expTrain, genes, groups, nRand=70, ntrees=2000, stratify=False): """Build random forest classifier - Args: + Parameteres + ----------- + expTrain: expTrain (pandas dataframe): Gene Expression matrix (raw counts) - genes (str): gene used for training - groups (str): annotation used for training - nRand (int): number of random cell profiles generated - ntrees (int): number of decision trees used in RandomForestClassifier - stratify (bool): whether to stratify samples for each class - Returns: + genes: str + gene used for training + groups: str + annotation used for training + nRand: int + number of random cell profiles generated + ntrees: int + number of decision trees used in RandomForestClassifier + stratify: bool + whether to stratify samples for each class + + Returns + ----------- RandomForestClassifier """ @@ -69,6 +87,34 @@ def sc_makeClassifier(expTrain, genes, groups, nRand=70, ntrees=2000, stratify=F return clf def scn_train(aTrain,dLevel,nTopGenes = 100,nTopGenePairs = 100,nRand = 100, nTrees = 1000,stratify=False,counts_per_cell_after=1e4, scaleMax=10, limitToHVG=False): + + """Train for pySCN classifier + Parameteres + ----------- + aTrain: + adata that contains training data for pySCN + dLevel: str + train labels + nTopGenes: int + numbers of top genes used to compute for topPairs + nTopGenePairs: int + number of topPairs used to construct pySCN classifier + ntrees: int + number of decision trees used in RandomForestClassifier + stratify: bool + whether to stratify samples for each class + counts_per_cell_after: int + total count after normalization + scaleMax: int + scaling factor + limitToHVG: bool + whether to limit the gene selection to highly_variable_genes + + Returns + ----------- + pySCN classifier + """ + warnings.filterwarnings('ignore') stTrain= aTrain.obs @@ -104,6 +150,27 @@ def scn_train(aTrain,dLevel,nTopGenes = 100,nTopGenePairs = 100,nRand = 100, nTr return [cgenesA, xpairs, tspRF] def scn_classify(adata, cgenes, xpairs, rf_tsp, nrand = 0 ): + + """Train for pySCN classifier + Parameteres + ----------- + adata: AnnData + query dataset + cgenes: str + intersected genes between query dataset and training dataset + xpairs: str + topPairs + rf_tsp: + pySCN classifier + nrand: int + random number of profiles generated for query + + Returns: + ------- + aNew:AnnData + SCN classification matrix + + """ classRes = scn_predict(cgenes, xpairs, rf_tsp, adata, nrand = nrand) categories = classRes.columns.values adNew = ad.AnnData(classRes, obs=adata.obs, var=pd.DataFrame(index=categories)) From fc0d36219c69f70b84f9a37c696e0bd4582d9c50 Mon Sep 17 00:00:00 2001 From: yuqiyuqitan Date: Thu, 15 Oct 2020 16:55:05 -0400 Subject: [PATCH 3/5] finish doc string for scn_train.py --- pySingleCellNet/scn_train.py | 39 ++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/pySingleCellNet/scn_train.py b/pySingleCellNet/scn_train.py index 92b98fe..3111ef6 100644 --- a/pySingleCellNet/scn_train.py +++ b/pySingleCellNet/scn_train.py @@ -151,7 +151,7 @@ def scn_train(aTrain,dLevel,nTopGenes = 100,nTopGenePairs = 100,nRand = 100, nTr def scn_classify(adata, cgenes, xpairs, rf_tsp, nrand = 0 ): - """Train for pySCN classifier + """Transform & predict labels with pySCN classifier and add the result to AnnData Parameteres ----------- adata: AnnData @@ -186,13 +186,32 @@ def add_classRes(adata: AnnData, adClassRes, copy=False) -> AnnData: adata.obs['SCN_class'] = adClassRes.obs['SCN_class'] return adata if copy else None -def check_adX(adata: AnnData) -> AnnData: +def check_adX(adata: AnnData) -> AnnData: from scipy import sparse if( isinstance(adata.X, np.ndarray)): adata.X = sparse.csr_matrix(adata.X) def scn_predict(cgenes, xpairs, rf_tsp, aDat, nrand = 2): + """Transform the query data and predict labels with pySCN classifier + Parameteres + ----------- + adata: AnnData + query dataset + cgenes: str + intersected genes between query dataset and training dataset + xpairs: str + topPairs + rf_tsp: + pySCN classifier + nrand: int + random number of profiles generated for query + + Returns: + ------- + classRes_val: + SCN classification matrix + """ ### expDat= pd.DataFrame(data=aDat.X, index= aDat.obs.index.values, columns= aDat.var.index.values) expDat= pd.DataFrame(data=aDat.X.toarray(), index= aDat.obs.index.values, columns= aDat.var.index.values) expValTrans=query_transform(expDat.reindex(labels=cgenes, axis='columns', fill_value=0), xpairs) @@ -200,6 +219,22 @@ def scn_predict(cgenes, xpairs, rf_tsp, aDat, nrand = 2): return classRes_val def rf_classPredict(rfObj,expQuery,numRand=50): + """Predict labels with pySCN classifier + Parameteres + ----------- + expQuery: + transformed query dataset + + rfObj: + pySCN classifier + numRand: int + random number of profiles generated for query + + Returns: + ------- + xpreds:pd DataFrame + SCN classification matrix + """ if numRand > 0 : randDat=randomize(expQuery, num=numRand) expQuery=pd.concat([expQuery, randDat]) From 214c5c9f71fe4e6641b46f2cc715bc16bfdc9f7f Mon Sep 17 00:00:00 2001 From: yuqiyuqitan Date: Thu, 15 Oct 2020 17:00:16 -0400 Subject: [PATCH 4/5] add some doc for tsp_rf.py --- pySingleCellNet/scn_train.py | 1 - pySingleCellNet/tsp_rf.py | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/pySingleCellNet/scn_train.py b/pySingleCellNet/scn_train.py index 3111ef6..0a98845 100644 --- a/pySingleCellNet/scn_train.py +++ b/pySingleCellNet/scn_train.py @@ -224,7 +224,6 @@ def rf_classPredict(rfObj,expQuery,numRand=50): ----------- expQuery: transformed query dataset - rfObj: pySCN classifier numRand: int diff --git a/pySingleCellNet/tsp_rf.py b/pySingleCellNet/tsp_rf.py index 1016eee..773ca02 100644 --- a/pySingleCellNet/tsp_rf.py +++ b/pySingleCellNet/tsp_rf.py @@ -8,6 +8,30 @@ def csRenameOrth(expQuery,expTrain,orthTable,speciesQuery='human',speciesTrain='mouse'): + + """Convert gene names betwen species + Parameteres + ----------- + expQuery: + Query gene expression matrix + expTrain: + train gene expression matrix + orthTable: + species conversion orthTable + speciesQuery: + indicate species of query 'human' or 'mouse' + speciesTrain: + indicate species of query 'human' or 'mouse' + + Returns: + -------- + expQ: + a species compatible conversion gene expression table + expT: + a species compatible conversion gene expression table + + """ + _,_,cgenes=np.intersect1d(expQuery.columns.values, orthTable[speciesQuery], return_indices=True) _,_,ccgenes=np.intersect1d(expTrain.columns.values, orthTable[speciesTrain], return_indices=True) temp1=np.zeros(len(orthTable.index.values), dtype=bool) From 8991f24bcb7a4919c940dbbf1099ded902a76cd0 Mon Sep 17 00:00:00 2001 From: yuqiyuqitan Date: Mon, 21 Dec 2020 14:00:23 -0500 Subject: [PATCH 5/5] correct typo for get_cate --- .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 273 bytes pySingleCellNet/__pycache__/plots.cpython-38.pyc | Bin 0 -> 6414 bytes .../__pycache__/scn_assess.cpython-38.pyc | Bin 0 -> 7299 bytes .../__pycache__/scn_train.cpython-38.pyc | Bin 0 -> 7778 bytes pySingleCellNet/__pycache__/stats.cpython-38.pyc | Bin 0 -> 7785 bytes .../__pycache__/tsp_rf.cpython-38.pyc | Bin 0 -> 6992 bytes pySingleCellNet/__pycache__/utils.cpython-38.pyc | Bin 0 -> 6599 bytes pySingleCellNet/plots.py | 2 +- 8 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 pySingleCellNet/__pycache__/__init__.cpython-38.pyc create mode 100644 pySingleCellNet/__pycache__/plots.cpython-38.pyc create mode 100644 pySingleCellNet/__pycache__/scn_assess.cpython-38.pyc create mode 100644 pySingleCellNet/__pycache__/scn_train.cpython-38.pyc create mode 100644 pySingleCellNet/__pycache__/stats.cpython-38.pyc create mode 100644 pySingleCellNet/__pycache__/tsp_rf.cpython-38.pyc create mode 100644 pySingleCellNet/__pycache__/utils.cpython-38.pyc diff --git a/pySingleCellNet/__pycache__/__init__.cpython-38.pyc b/pySingleCellNet/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23123547d96a129a17e19d1c4a8d2b5c607ef71e GIT binary patch literal 273 zcmWIL<>g`k0^UQs@qIx0F^GcQTp~b01#rh?Qd6lJwnfmUTB_5?o`T>=}nR)3s usm`f6Iew`n`UPnG`1s7c%#!$cy@JYH95%W6DWy57b|8lpgZ$6I2!jAD3Pq0q literal 0 HcmV?d00001 diff --git a/pySingleCellNet/__pycache__/plots.cpython-38.pyc b/pySingleCellNet/__pycache__/plots.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14a68f6e242609d047452bc4dae738958d483f9a GIT binary patch literal 6414 zcmaJ_+ix7#eV+Tw&R)2@h$1P9re(YBrm0mcZ7zml%9bNLjm&SJhf_dS>2?>pZ)FW2j?g2(;y-|M`&tSIkMLg*ITK@~oG6SGd>rQOxuhV#4iuncquz;HPc7$CKgUnZ35z&7O`T zq3?M7P~mm-;Qgo0znuC!J)e2;-tb0n{<$D~ez89neiW_y+4;f!@hHjC z#|L|&nsz%|{cBw>9_-a6FK)bEoR!3&87M0vY87tTLtCqkSPSd4+RWY8@Xw8KDr{oSvFdo;ePiUH%yco6JFLE_;QuOlrI=+6 z>L)^TY)Xdg@YKl7-C`bnKaHL$;X=5G-pcVHeW^^5W!>UT4=tg-@vQRN%BYHeSPsv^ z!s@x1R~MDMig0t^RFxY)p}b<{KzUUg)kgKa5_0S{P^iYi%FoOxN0#KPS|>{fzkbZChxPi}U)(J;+?E~>ECNu-g& z%9zJlC+&KX4;!V-BuaQHjBei>2&cQ@@y_~^xG zcITayH~#yx@BIGSxFg?L8~6LWYj^(r-{|?TwQ=F^uig3l+}qFn@YdIkYJKY{>ql#? z$Aoj!<1xH36^+dxLMJD2EglGK7za0ozA#wO&4hj{7zjNDLDc0)I|EE~mSOoKKNdR8 zu{&NH3ai(JhIXyt0HlEh>3i7#o*e}n!s%h9(}CX=&T|M|qmaHC^hDKTSis>16%+=Q z5{9=6n{d6|&X(_G@=sDf>+EGgcN?vH+>3iYw=k>R28gLiu3QIHrG(wG_%xb9-#cJW z#*-fARp_t^vDibI$nPP!vjsbf{IoN!4zi_Wp?AGqQKPm7Ng7Do)CW-}Y8lxDO7A5@ z^f}#1ZXQmxoo?i%>5HBm#UO1P-K)t>;N>K0o4pR^we9xzj%H9Apip~4@1+-n?(JR> znzze;k;Y?>`vcY|yGq-7)MJugOK4D``ipz8dywU2`rA1qr56E;>f*PA*HBlr1+}Tp z;iWW7T>y`|s)M+uXg_tdhB~j-@LKA;HivZXqyEpQ*f+-%J0?~w(?P68VKOzd?ju%v zLabLaBP24bNMh9sd*x zO-ZeFHaj8KtCBpgqWntn5Zam}YQ2Y8<8j{Ul3Gs?Ygywo1j6!|SV6#&SXYa46JkBP z&Zn_<{2ajy!7Kq;1wTaqC8Y4P1SAyrLj;cyNIK(>Qt&Z?7Qu%IewpCI1WyoLAovKu zuMzw@!AA)`M(}ZhPXJnva56*wNrI;c)(9>UJWcQn0d3j*S%OayTp@Ul;CX^i6Z{6j zX9!*(c#&Y8;5P|gBKR!9ga-Ikie4kQPVh3pZxQ@9!7Bv6L(nGp9KovuuMv=VIpJLH zv1id3(POi?odMu3RHPR%k5m>Ha9hBJ_qh-=g>2yJ7f;rPR4Slr$N!6CRC2r zQ<(u(=;UTk+1B|Nb0@U$Rtp&U0cl~hTDC{2a4++slEL}1DRT2{3jfl~rt`{eH8;*c zhunrrVs>sYXHL04kJ2_ZR@pb9KipBZtjF|i16t$lYzE{ZTe_`5$^7%Emd%E9QZt)n zuV^fJC5MA=YsWpytKlh1gEQIq)%P7Jn{iq(KX&}1_~`?sSRhxKlvu

OD=m{86c; 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 Rx%!Ls3-yyT)hVZb;@`3?H?sf$ literal 0 HcmV?d00001 diff --git a/pySingleCellNet/__pycache__/stats.cpython-38.pyc b/pySingleCellNet/__pycache__/stats.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf44fa06ad811bd44f113d1ebc6d0c9fb058fccf GIT binary patch literal 7785 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 literal 0 HcmV?d00001 diff --git a/pySingleCellNet/__pycache__/tsp_rf.cpython-38.pyc b/pySingleCellNet/__pycache__/tsp_rf.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bd335d87f925199537a7af87b49a6f8ee901e01 GIT binary patch literal 6992 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: for i in range(0, len(res)):