We read in input.scone.csv, which is our file modified (and renamed) from the get.marker.names() function. The K-nearest neighbor generation is derived from the Fast Nearest Neighbors (FNN) R package, within our function Fnn(), which takes as input the “input markers” to be used, along with the concatenated data previously generated, and the desired k. We advise the default selection to the total number of cells in the dataset divided by 100, as has been optimized on existing mass cytometry datasets. The output of this function is a matrix of each cell and the identity of its k-nearest neighbors, in terms of its row number in the dataset used here as input.
library(Sconify)
# Markers from the user-generated excel file
marker.file <- system.file('extdata', 'markers.csv', package = "Sconify")
markers <- ParseMarkers(marker.file)
# How to convert your excel sheet into vector of static and functional markers
markers
## $input
## [1] "CD3(Cd110)Di" "CD3(Cd111)Di" "CD3(Cd112)Di"
## [4] "CD235-61-7-15(In113)Di" "CD3(Cd114)Di" "CD45(In115)Di"
## [7] "CD19(Nd142)Di" "CD22(Nd143)Di" "IgD(Nd145)Di"
## [10] "CD79b(Nd146)Di" "CD20(Sm147)Di" "CD34(Nd148)Di"
## [13] "CD179a(Sm149)Di" "CD72(Eu151)Di" "IgM(Eu153)Di"
## [16] "Kappa(Sm154)Di" "CD10(Gd156)Di" "Lambda(Gd157)Di"
## [19] "CD24(Dy161)Di" "TdT(Dy163)Di" "Rag1(Dy164)Di"
## [22] "PreBCR(Ho165)Di" "CD43(Er167)Di" "CD38(Er168)Di"
## [25] "CD40(Er170)Di" "CD33(Yb173)Di" "HLA-DR(Yb174)Di"
##
## $functional
## [1] "pCrkL(Lu175)Di" "pCREB(Yb176)Di" "pBTK(Yb171)Di" "pS6(Yb172)Di"
## [5] "cPARP(La139)Di" "pPLCg2(Pr141)Di" "pSrc(Nd144)Di" "Ki67(Sm152)Di"
## [9] "pErk12(Gd155)Di" "pSTAT3(Gd158)Di" "pAKT(Tb159)Di" "pBLNK(Gd160)Di"
## [13] "pP38(Tm169)Di" "pSTAT5(Nd150)Di" "pSyk(Dy162)Di" "tIkBa(Er166)Di"
# Get the particular markers to be used as knn and knn statistics input
input.markers <- markers[[1]]
funct.markers <- markers[[2]]
# Selection of the k. See "Finding Ideal K" vignette
k <- 30
# The built-in scone functions
wand.nn <- Fnn(cell.df = wand.combined, input.markers = input.markers, k = k)
# Cell identity is in rows, k-nearest neighbors are columns
# List of 2 includes the cell identity of each nn,
# and the euclidean distance between
# itself and the cell of interest
# Indices
str(wand.nn[[1]])
## int [1:1000, 1:30] 794 90 881 600 730 450 860 295 633 699 ...
wand.nn[[1]][1:20, 1:10]
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,] 794 138 207 187 352 446 852 735 602 272
## [2,] 90 889 561 944 995 799 729 863 575 25
## [3,] 881 169 318 718 658 667 834 145 571 505
## [4,] 600 35 612 942 826 282 987 738 210 106
## [5,] 730 455 14 266 244 658 688 667 809 976
## [6,] 450 684 865 704 336 176 547 52 348 716
## [7,] 860 509 542 208 111 667 688 521 815 611
## [8,] 295 760 549 479 252 241 962 342 769 970
## [9,] 633 214 36 811 573 310 462 693 514 498
## [10,] 699 39 103 690 519 639 447 207 290 962
## [11,] 655 931 845 992 605 86 337 908 482 402
## [12,] 339 278 855 960 504 643 531 836 184 956
## [13,] 615 42 76 714 19 656 836 869 735 988
## [14,] 738 540 658 771 904 166 801 111 452 667
## [15,] 866 607 449 956 230 669 982 126 685 339
## [16,] 519 568 428 533 291 958 403 334 896 872
## [17,] 747 671 945 445 614 686 630 395 753 761
## [18,] 87 930 647 22 917 176 322 488 376 341
## [19,] 405 445 13 630 76 751 869 42 747 524
## [20,] 667 688 244 658 282 738 809 627 730 477
# Distance
str(wand.nn[[2]])
## num [1:1000, 1:30] 2.88 2.21 4.54 3.32 4.53 ...
wand.nn[[2]][1:20, 1:10]
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 2.876851 3.230275 3.231254 3.292981 3.407014 3.443274 3.570165 3.579812
## [2,] 2.213633 2.370185 2.693816 2.841634 2.859277 2.904089 3.081602 3.096632
## [3,] 4.535107 4.933615 5.067362 5.081027 5.141806 5.161890 5.243734 5.307418
## [4,] 3.321649 3.452362 3.490052 3.572306 3.637633 3.660755 3.673769 3.731091
## [5,] 4.534855 4.652209 4.700434 4.787138 4.799307 4.936175 5.022785 5.035981
## [6,] 3.037649 3.369404 3.487481 3.589727 3.601190 3.619865 3.663499 3.716100
## [7,] 3.754009 3.952653 4.491266 4.506636 4.685829 4.791210 4.811563 4.898619
## [8,] 4.514579 4.732992 5.055575 5.144416 5.153642 5.163699 5.177355 5.194464
## [9,] 4.300989 4.333011 4.343596 4.371364 4.436539 4.453613 4.487755 4.570057
## [10,] 3.025722 3.261289 3.356304 3.627670 3.671106 3.720092 3.740435 3.763745
## [11,] 4.249507 4.817943 4.975306 4.983098 5.097261 5.131622 5.333571 5.372617
## [12,] 3.135347 3.395116 3.412474 3.549475 3.585570 3.589862 3.591775 3.617930
## [13,] 2.567725 2.575365 2.696979 2.769046 2.798915 2.862286 2.873548 2.874440
## [14,] 3.327672 3.496007 3.600312 3.662678 3.692267 3.793516 3.938747 4.016861
## [15,] 2.847317 3.189816 3.193380 3.297860 3.443673 3.487595 3.580327 3.771578
## [16,] 3.781681 4.238408 4.372341 4.621226 4.739013 4.741922 4.761297 4.765291
## [17,] 2.490997 2.728539 2.761806 2.800912 2.821396 2.887494 2.940268 2.948158
## [18,] 3.977072 4.141066 4.246897 4.313911 4.317570 4.338693 4.347362 4.368750
## [19,] 2.545865 2.705539 2.798915 2.900180 2.995501 3.009345 3.019087 3.040590
## [20,] 4.067747 4.321824 4.555948 4.646032 4.718562 4.823128 4.858189 4.917033
## [,9] [,10]
## [1,] 3.584089 3.588584
## [2,] 3.179710 3.205984
## [3,] 5.322587 5.352871
## [4,] 3.827220 3.932257
## [5,] 5.090182 5.159841
## [6,] 3.754625 3.760595
## [7,] 4.910816 4.912577
## [8,] 5.207046 5.233239
## [9,] 4.575667 4.619212
## [10,] 3.778993 3.843476
## [11,] 5.379952 5.408303
## [12,] 3.633145 3.640325
## [13,] 2.885491 2.892904
## [14,] 4.042424 4.064575
## [15,] 3.773533 3.783298
## [16,] 4.803227 4.864673
## [17,] 2.988546 2.988839
## [18,] 4.388408 4.389687
## [19,] 3.058576 3.079463
## [20,] 4.950909 4.953250
This function iterates through each KNN, and performs a series of calculations. The first is fold change values for each maker per KNN, where the user chooses whether this will be based on medians or means. The second is a statistical test, where the user chooses t test or Mann-Whitney U test. I prefer the latter, because it does not assume any properties of the distributions. Of note, the p values are adjusted for false discovery rate, and therefore are called q values in the output of this function. The user also inputs a threshold parameter (default 0.05), where the fold change values will only be shown if the corresponding statistical test returns a q value below said threshold. Finally, the “multiple.donor.compare” option, if set to TRUE will perform a t test based on the mean per-marker values of each donor. This is to allow the user to make comparisons across replicates or multiple donors if that is relevant to the user’s biological questions. This function returns a matrix of cells by computed values (change and statistical test results, labeled either marker.change or marker.qvalue). This matrix is intermediate, as it gets concatenated with the original input matrix in the post-processing step (see the relevant vignette). We show the code and the output below. See the post-processing vignette, where we show how this gets combined with the input data, and additional analysis is performed.
wand.scone <- SconeValues(nn.matrix = wand.nn,
cell.data = wand.combined,
scone.markers = funct.markers,
unstim = "basal")
wand.scone
## # A tibble: 1,000 × 34
## `pCrkL(Lu175)Di.IL7.qvalue` pCREB(Yb176)Di.IL7.qvalu…¹ pBTK(Yb171)Di.IL7.qv…²
## <dbl> <dbl> <dbl>
## 1 0.431 1 0.790
## 2 0.685 1 0.698
## 3 0.924 0.993 0.841
## 4 0.532 1 0.790
## 5 0.851 0.930 0.981
## 6 0.666 1 0.866
## 7 0.685 0.967 0.790
## 8 0.765 0.930 0.979
## 9 0.789 0.930 0.915
## 10 0.448 0.967 0.829
## # ℹ 990 more rows
## # ℹ abbreviated names: ¹`pCREB(Yb176)Di.IL7.qvalue`,
## # ²`pBTK(Yb171)Di.IL7.qvalue`
## # ℹ 31 more variables: `pS6(Yb172)Di.IL7.qvalue` <dbl>,
## # `cPARP(La139)Di.IL7.qvalue` <dbl>, `pPLCg2(Pr141)Di.IL7.qvalue` <dbl>,
## # `pSrc(Nd144)Di.IL7.qvalue` <dbl>, `Ki67(Sm152)Di.IL7.qvalue` <dbl>,
## # `pErk12(Gd155)Di.IL7.qvalue` <dbl>, `pSTAT3(Gd158)Di.IL7.qvalue` <dbl>, …
If one wants to export KNN data to perform other statistics not available in this package, then I provide a function that produces a list of each cell identity in the original input data matrix, and a matrix of all cells x features of its KNN.
I also provide a function to find the KNN density estimation independently of the rest of the “scone.values” analysis, to save time if density is all the user wants. With this density estimation, one can perform interesting analysis, ranging from understanding phenotypic density changes along a developmental progression (see post-processing vignette for an example), to trying out density-based binning methods (eg. X-shift). Of note, this density is specifically one divided by the aveage distance to k-nearest neighbors. This specific measure is related to the Shannon Entropy estimate of that point on the manifold (https://hal.archives-ouvertes.fr/hal-01068081/document).
I use this metric to avoid the unusual properties of the volume of a sphere as it increases in dimensions (https://en.wikipedia.org/wiki/Volume_of_an_n-ball). This being said, one can modify this vector to be such a density estimation (example http://www.cs.haifa.ac.il/~rita/ml_course/lectures_old/KNN.pdf), by treating the distance to knn as the radius of a n-dimensional sphere and incoroprating said volume accordingly.
An individual with basic programming skills can iterate through these elements to perform the statistics of one’s choosing. Examples would include per-KNN regression and classification, or feature imputation. The additional functionality is shown below, with the example knn.list in the package being the first ten instances:
# Constructs KNN list, computes KNN density estimation
wand.knn.list <- MakeKnnList(cell.data = wand.combined, nn.matrix = wand.nn)
wand.knn.list[[8]]
## # A tibble: 30 × 51
## `CD3(Cd110)Di` `CD3(Cd111)Di` `CD3(Cd112)Di` `CD235-61-7-15(In113)Di`
## <dbl> <dbl> <dbl> <dbl>
## 1 0.0745 -0.523 -0.316 0.207
## 2 -0.226 0.115 -0.957 0.138
## 3 -0.194 -0.374 -0.562 -0.702
## 4 -0.348 -0.638 -0.770 0.363
## 5 -0.0893 -0.0653 -0.0490 -0.135
## 6 -0.223 -0.865 -1.16 -1.42
## 7 -0.199 -0.0196 0.0463 -1.03
## 8 -0.0210 -0.0601 0.432 -0.906
## 9 0.00845 -0.103 0.128 0.0507
## 10 -0.137 -0.696 0.523 0.598
## # ℹ 20 more rows
## # ℹ 47 more variables: `CD3(Cd114)Di` <dbl>, `CD45(In115)Di` <dbl>,
## # `CD19(Nd142)Di` <dbl>, `CD22(Nd143)Di` <dbl>, `IgD(Nd145)Di` <dbl>,
## # `CD79b(Nd146)Di` <dbl>, `CD20(Sm147)Di` <dbl>, `CD34(Nd148)Di` <dbl>,
## # `CD179a(Sm149)Di` <dbl>, `CD72(Eu151)Di` <dbl>, `IgM(Eu153)Di` <dbl>,
## # `Kappa(Sm154)Di` <dbl>, `CD10(Gd156)Di` <dbl>, `Lambda(Gd157)Di` <dbl>,
## # `CD24(Dy161)Di` <dbl>, `TdT(Dy163)Di` <dbl>, `Rag1(Dy164)Di` <dbl>, …
# Finds the KNN density estimation for each cell, ordered by column, in the
# original data matrix
wand.knn.density <- GetKnnDe(nn.matrix = wand.nn)
str(wand.knn.density)
## num [1:1000] 0.276 0.307 0.177 0.248 0.189 ...